Bug 17553: Move existing tests
[koha-ffzg.git] / t / db_dependent / Koha / Patrons.t
index f666507..40d36ee 100644 (file)
 
 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;
@@ -51,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(
@@ -64,7 +70,7 @@ 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' );
 
@@ -182,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;
@@ -200,13 +204,11 @@ subtest 'is_expired' => sub {
 };
 
 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;
@@ -241,84 +243,95 @@ subtest 'is_going_to_expire' => sub {
 
 
 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 => 3;
+    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} );
@@ -327,7 +340,9 @@ subtest "move_to_deleted" => sub {
     my $deleted_patron = $schema->resultset('Deletedborrower')
         ->search( { borrowernumber => $patron->{borrowernumber} }, { result_class => 'DBIx::Class::ResultClass::HashRefInflator' } )
         ->next;
-    isnt( $deleted_patron->{updated_on}, $retrieved_patron->{updated_on}, 'Koha::Patron->move_to_deleted should have correctly updated the updated_on column');
+    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
@@ -365,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',
@@ -384,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);
@@ -399,24 +414,23 @@ 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' => 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, '' );
@@ -426,7 +440,9 @@ subtest 'checkouts + get_overdues' => sub {
             value  => {
                 homebranch    => $library->{branchcode},
                 holdingbranch => $library->{branchcode},
-                biblionumber  => $biblionumber_1
+                biblionumber  => $biblionumber_1,
+                itemlost      => 0,
+                withdrawn     => 0,
             }
         }
     );
@@ -436,7 +452,9 @@ subtest 'checkouts + get_overdues' => sub {
             value  => {
                 homebranch    => $library->{branchcode},
                 holdingbranch => $library->{branchcode},
-                biblionumber  => $biblionumber_1
+                biblionumber  => $biblionumber_1,
+                itemlost      => 0,
+                withdrawn     => 0,
             }
         }
     );
@@ -447,7 +465,9 @@ subtest 'checkouts + get_overdues' => sub {
             value  => {
                 homebranch    => $library->{branchcode},
                 holdingbranch => $library->{branchcode},
-                biblionumber  => $biblionumber_2
+                biblionumber  => $biblionumber_2,
+                itemlost      => 0,
+                withdrawn     => 0,
             }
         }
     );
@@ -462,9 +482,12 @@ subtest 'checkouts + get_overdues' => sub {
     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} } } );
@@ -484,30 +507,40 @@ subtest 'checkouts + get_overdues' => sub {
     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 => 6;
+    plan tests => 7;
 
     my $patron = $builder->build( { source => 'Borrower' } );
     $patron = Koha::Patrons->find( $patron->{borrowernumber} );
 
     my $today = dt_from_string;
 
-    $patron->dateofbirth( $today->clone->add( years => -12, months => -6, days => -1 ) );
+    $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 ) );
+    $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;
@@ -597,8 +630,8 @@ subtest 'search_upcoming_membership_expires' => sub {
     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, '' );
@@ -655,13 +688,537 @@ subtest 'holds' => sub {
     $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,
+        '',                      ''
+    );
+}