3 # Some tests for SIP::ILS::Patron
4 # This needs to be extended! Your help is appreciated..
7 use Test::More tests => 10;
10 use t::lib::TestBuilder;
12 use C4::SIP::ILS::Patron;
13 use Koha::Account::Lines;
15 use Koha::DateUtils qw( dt_from_string output_pref );
16 use Koha::Patron::Attributes;
19 my $schema = Koha::Database->new->schema;
20 $schema->storage->txn_begin;
22 my $builder = t::lib::TestBuilder->new();
23 my $patron1 = $builder->build({ source => 'Borrower' });
24 my $card = $patron1->{cardnumber};
26 # Check existing card number
27 my $sip_patron = C4::SIP::ILS::Patron->new( $card );
28 is( defined $sip_patron, 1, "Patron is valid" );
30 # Check invalid cardnumber by deleting patron
31 $schema->resultset('Borrower')->search({ cardnumber => $card })->delete;
32 my $sip_patron2 = C4::SIP::ILS::Patron->new( $card );
33 is( $sip_patron2, undef, "Patron is not valid (anymore)" );
35 subtest "new tests" => sub {
39 my $patron = $builder->build(
45 my $cardnumber = $patron->{cardnumber};
46 my $userid = $patron->{userid};
47 my $borrowernumber = $patron->{borrowernumber};
49 my $ils_patron = C4::SIP::ILS::Patron->new($cardnumber);
50 is( ref($ils_patron), 'C4::SIP::ILS::Patron', 'Found patron via cardnumber scalar' );
51 $ils_patron = C4::SIP::ILS::Patron->new($userid);
52 is( ref($ils_patron), 'C4::SIP::ILS::Patron', 'Found patron via userid scalar' );
53 $ils_patron = C4::SIP::ILS::Patron->new( { borrowernumber => $borrowernumber } );
54 is( ref($ils_patron), 'C4::SIP::ILS::Patron', 'Found patron via borrowernumber hashref' );
55 $ils_patron = C4::SIP::ILS::Patron->new( { cardnumber => $cardnumber } );
56 is( ref($ils_patron), 'C4::SIP::ILS::Patron', 'Found patron via cardnumber hashref' );
57 $ils_patron = C4::SIP::ILS::Patron->new( { userid => $userid } );
58 is( ref($ils_patron), 'C4::SIP::ILS::Patron', 'Found patron via userid hashref' );
61 subtest "OverduesBlockCirc tests" => sub {
65 my $odue_patron = $builder->build(
69 dateexpiry => "3000-01-01",
73 my $good_patron = $builder->build(
77 dateexpiry => "3000-01-01",
81 my $odue = $builder->build({ source => 'Issue', value => {
82 borrowernumber => $odue_patron->{borrowernumber},
83 date_due => '2017-01-01',
86 t::lib::Mocks::mock_preference( 'OverduesBlockCirc', 'noblock' );
87 my $odue_sip_patron = C4::SIP::ILS::Patron->new( $odue_patron->{cardnumber} );
88 is( $odue_sip_patron->{charge_ok}, 1, "Not blocked with overdues when set to 'Don't block'");
89 $odue_sip_patron = C4::SIP::ILS::Patron->new( $good_patron->{cardnumber} );
90 is( $odue_sip_patron->{charge_ok}, 1, "Not blocked without overdues when set to 'Don't block'");
92 t::lib::Mocks::mock_preference( 'OverduesBlockCirc', 'confirmation' );
93 $odue_sip_patron = C4::SIP::ILS::Patron->new( $odue_patron->{cardnumber} );
94 is( $odue_sip_patron->{charge_ok}, '', "Blocked with overdues when set to 'Ask for confirmation'");
95 $odue_sip_patron = C4::SIP::ILS::Patron->new( $good_patron->{cardnumber} );
96 is( $odue_sip_patron->{charge_ok}, 1, "Not blocked without overdues when set to 'confirmation'");
98 t::lib::Mocks::mock_preference( 'OverduesBlockCirc', 'block' );
99 $odue_sip_patron = C4::SIP::ILS::Patron->new( $odue_patron->{cardnumber} );
100 is( $odue_sip_patron->{charge_ok}, '', "Blocked with overdues when set to 'Block'");
101 $odue_sip_patron = C4::SIP::ILS::Patron->new( $good_patron->{cardnumber} );
102 is( $odue_sip_patron->{charge_ok}, 1, "Not blocked without overdues when set to 'Block'");
106 subtest "Test build_patron_attribute_string" => sub {
110 my $patron = $builder->build( { source => 'Borrower' } );
112 my $attribute_type = $builder->build( { source => 'BorrowerAttributeType' } );
113 my $attribute = Koha::Patron::Attribute->new(
115 borrowernumber => $patron->{borrowernumber},
116 code => $attribute_type->{code},
117 attribute => 'Test Attribute'
121 my $attribute_type2 = $builder->build( { source => 'BorrowerAttributeType' } );
122 my $attribute2 = Koha::Patron::Attribute->new(
124 borrowernumber => $patron->{borrowernumber},
125 code => $attribute_type2->{code},
126 attribute => 'Another Test Attribute'
130 my $ils_patron = C4::SIP::ILS::Patron->new( $patron->{cardnumber} );
133 $server->{account}->{patron_attribute}->{code} = $attribute->code;
134 $server->{account}->{patron_attribute}->{field} = 'XY';
135 my $attribute_string = $ils_patron->build_patron_attributes_string( $server );
136 is( $attribute_string, "XYTest Attribute|", 'Attribute field generated correctly with single param' );
139 $server->{account}->{patron_attribute}->[0]->{code} = $attribute->code;
140 $server->{account}->{patron_attribute}->[0]->{field} = 'XY';
141 $server->{account}->{patron_attribute}->[1]->{code} = $attribute2->code;
142 $server->{account}->{patron_attribute}->[1]->{field} = 'YZ';
143 $attribute_string = $ils_patron->build_patron_attributes_string( $server );
144 is( $attribute_string, "XYTest Attribute|YZAnother Test Attribute|", 'Attribute field generated correctly with multiple params' );
147 subtest "Test build_custom_field_string" => sub {
151 my $patron = $builder->build_object( { class => 'Koha::Patrons',value=>{surname => "Duck", firstname => "Darkwing"} } );
154 my $ils_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );
157 $server->{account}->{custom_patron_field}->{field} = "DW";
158 my $attribute_string = $ils_patron->build_custom_field_string( $server );
159 is( $attribute_string, "", 'Custom field not generated if no value passed' );
162 $server->{account}->{custom_patron_field}->{template} = "[% patron.surname %]";
163 $attribute_string = $ils_patron->build_custom_field_string( $server );
164 is( $attribute_string, "", 'Custom field not generated if no field passed' );
168 $server->{account}->{custom_patron_field}->{field} = "DW";
169 $server->{account}->{custom_patron_field}->{template} = "[% patron.firstname %] [% patron.surname %], let's get dangerous!";
170 $attribute_string = $ils_patron->build_custom_field_string( $server );
171 is( $attribute_string, "DWDarkwing Duck, let's get dangerous!|", 'Custom field processed correctly' );
174 $server->{account}->{custom_patron_field}->[0]->{field} = "DW";
175 $server->{account}->{custom_patron_field}->[0]->{template} = "[% patron.firstname %] [% patron.surname %], let's get dangerous!";
176 $server->{account}->{custom_patron_field}->[1]->{field} = "LM";
177 $server->{account}->{custom_patron_field}->[1]->{template} = "Launchpad McQuack crashed on [% patron.dateexpiry %]";
178 $attribute_string = $ils_patron->build_custom_field_string( $server );
179 is( $attribute_string, "DWDarkwing Duck, let's get dangerous!|LMLaunchpad McQuack crashed on ".$patron->dateexpiry."|", 'Custom fields processed correctly when multiple exist' );
182 $server->{account}->{custom_patron_field}->[0]->{field} = "DW";
183 $server->{account}->{custom_patron_field}->[0]->{template} = "[% IF (patron.firstname) %] patron.surname, let's get dangerous!";
184 $server->{account}->{custom_patron_field}->[1]->{field} = "LM";
185 $server->{account}->{custom_patron_field}->[1]->{template} = "Launchpad McQuack crashed on [% patron.dateexpiry %]";
186 $attribute_string = $ils_patron->build_custom_field_string( $server );
187 is( $attribute_string, "LMLaunchpad McQuack crashed on ".$patron->dateexpiry."|", 'Custom fields processed correctly, bad template generate no text' );
191 subtest "update_lastseen tests" => sub {
194 my $seen_patron = $builder->build(
196 source => 'Borrower',
198 lastseen => "2001-01-01",
202 my $sip_patron = C4::SIP::ILS::Patron->new( $seen_patron->{cardnumber} );
203 t::lib::Mocks::mock_preference( 'TrackLastPatronActivity', '' );
204 $sip_patron->update_lastseen();
205 $seen_patron = Koha::Patrons->find({ cardnumber => $seen_patron->{cardnumber} });
206 is( output_pref({str => $seen_patron->lastseen(), dateonly => 1}), output_pref({str => '2001-01-01', dateonly => 1}),'Last seen not updated if not tracking patrons');
207 t::lib::Mocks::mock_preference( 'TrackLastPatronActivity', '1' );
208 $sip_patron->update_lastseen();
209 $seen_patron = Koha::Patrons->find({ cardnumber => $seen_patron->cardnumber() });
210 is( output_pref({str => $seen_patron->lastseen(), dateonly => 1}), output_pref({dt => dt_from_string(), dateonly => 1}),'Last seen updated to today if tracking patrons');
213 subtest "fine_items tests" => sub {
217 my $patron = $builder->build(
219 source => 'Borrower',
223 my $fee1 = $builder->build(
225 source => 'Accountline',
227 borrowernumber => $patron->{borrowernumber},
228 amountoutstanding => 1,
233 my $fee2 = $builder->build(
235 source => 'Accountline',
237 borrowernumber => $patron->{borrowernumber},
238 amountoutstanding => 1,
243 my $sip_patron = C4::SIP::ILS::Patron->new( $patron->{cardnumber} );
245 my $all_fine_items = $sip_patron->fine_items;
246 is( @$all_fine_items, 2, "Got all fine items" );
248 # Should return only the first fine item
249 my $fine_items = $sip_patron->fine_items(1,1);
250 is( @$fine_items, 1, "Got one fine item" );
251 is( $fine_items->[0]->{barcode}, $all_fine_items->[0]->{barcode}, "Got correct fine item");
253 # Should return only the second fine item
254 $fine_items = $sip_patron->fine_items(2,2);
255 is( @$fine_items, 1, "Got one fine item" );
256 is( $fine_items->[0]->{barcode}, $all_fine_items->[1]->{barcode}, "Got correct fine item");
258 # Should return all fine items
259 $fine_items = $sip_patron->fine_items(1,2);
260 is( @$fine_items, 2, "Got two fine items" );
261 is( $fine_items->[0]->{barcode}, $all_fine_items->[0]->{barcode}, "Got correct first fine item");
262 is( $fine_items->[1]->{barcode}, $all_fine_items->[1]->{barcode}, "Got correct second fine item");
264 # Check an invalid end boundary
265 $fine_items = $sip_patron->fine_items(1,99);
266 is( @$fine_items, 2, "Got two fine items" );
267 is( $fine_items->[0]->{barcode}, $all_fine_items->[0]->{barcode}, "Got correct first fine item");
268 is( $fine_items->[1]->{barcode}, $all_fine_items->[1]->{barcode}, "Got correct second fine item");
270 # Check an invalid start boundary
271 $fine_items = $sip_patron->fine_items(98,99);
272 is( @$fine_items, 0, "Got zero fine items" );
275 $schema->storage->txn_rollback;
277 subtest "NoIssuesChargeGuarantees tests" => sub {
281 t::lib::Mocks::mock_preference( 'borrowerRelationship', 'parent' );
283 $schema->storage->txn_begin;
285 my $patron = $builder->build_object({ class => 'Koha::Patrons' });
286 my $child = $builder->build_object({ class => 'Koha::Patrons' });
287 my $sibling = $builder->build_object({ class => 'Koha::Patrons' });
288 $child->add_guarantor({ guarantor_id => $patron->borrowernumber, relationship => 'parent' });
289 $sibling->add_guarantor({ guarantor_id => $patron->borrowernumber, relationship => 'parent' });
291 t::lib::Mocks::mock_preference('noissuescharge', 50);
292 t::lib::Mocks::mock_preference('NoIssuesChargeGuarantees', 11.01);
293 t::lib::Mocks::mock_preference('NoIssuesChargeGuarantorsWithGuarantees', undef);
295 my $fee1 = $builder->build_object(
297 class => 'Koha::Account::Lines',
299 borrowernumber => $patron->borrowernumber,
300 amountoutstanding => 11,
301 debit_type_code => 'OVERDUE',
306 my $fee2 = $builder->build_object(
308 class => 'Koha::Account::Lines',
310 borrowernumber => $child->borrowernumber,
311 amountoutstanding => 0.11,
312 debit_type_code => 'OVERDUE',
317 my $fee3 = $builder->build_object(
319 class => 'Koha::Account::Lines',
321 borrowernumber => $sibling->borrowernumber,
322 amountoutstanding => 11.11,
323 debit_type_code => 'OVERDUE',
328 my $sip_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );
330 is( $sip_patron->fines_amount, 11, "Only patron's fines are reported in total");
331 ok( !$sip_patron->charge_ok, "Guarantor blocked");
332 like( $sip_patron->screen_msg, qr/Patron blocked by fines \(22\.22\) on guaranteed accounts/,"Screen message includes related fines total");
334 $sip_patron = C4::SIP::ILS::Patron->new( $child->cardnumber );
336 is( $sip_patron->fines_amount, 0.11,"Guarantee only fines correctly counted");
337 ok( $sip_patron->charge_ok, "Guarantee not blocked by guarantor fines");
338 unlike( $sip_patron->screen_msg, qr/Patron blocked by fines .* on guaranteed accounts/,"Screen message does not include blocked message");
340 $schema->storage->txn_rollback;
343 subtest "NoIssuesChargeGuarantorsWithGuarantees tests" => sub {
347 t::lib::Mocks::mock_preference( 'borrowerRelationship', 'parent' );
349 $schema->storage->txn_begin;
351 my $patron = $builder->build_object({ class => 'Koha::Patrons' });
352 my $child = $builder->build_object({ class => 'Koha::Patrons' });
353 $child->add_guarantor({ guarantor_id => $patron->borrowernumber, relationship => 'parent' });
355 t::lib::Mocks::mock_preference('noissuescharge', 50);
356 t::lib::Mocks::mock_preference('NoIssuesChargeGuarantorsWithGuarantees', 11.01);
358 my $fee1 = $builder->build_object(
360 class => 'Koha::Account::Lines',
362 borrowernumber => $patron->borrowernumber,
363 amountoutstanding => 11,
364 debit_type_code => 'OVERDUE',
369 my $fee2 = $builder->build_object(
371 class => 'Koha::Account::Lines',
373 borrowernumber => $child->borrowernumber,
374 amountoutstanding => 0.11,
375 debit_type_code => 'OVERDUE',
380 my $sip_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );
382 is( $sip_patron->fines_amount, 11, "Personal fines correctly reported");
383 ok( !$sip_patron->charge_ok, "Guarantor blocked");
384 like( $sip_patron->screen_msg, qr/Patron blocked by fines \(11\.11\) on related accounts/,"Screen message includes related fines total");
386 $sip_patron = C4::SIP::ILS::Patron->new( $child->cardnumber );
388 is( $sip_patron->fines_amount, 0.11, "Personal fines correctly reported");
389 ok( !$sip_patron->charge_ok, "Guarantee blocked");
390 like( $sip_patron->screen_msg, qr/Patron blocked by fines \(11\.11\) on related accounts/,"Screen message includes related fines total");
392 t::lib::Mocks::mock_preference('NoIssuesChargeGuarantorsWithGuarantees', 12.01);
394 $sip_patron = C4::SIP::ILS::Patron->new( $child->cardnumber );
396 is( $sip_patron->fines_amount, 0.11, "Personal fines correctly reported");
397 ok( $sip_patron->charge_ok, "Guarantee not blocked");
398 unlike( $sip_patron->screen_msg, qr/Patron blocked by fines .* on related accounts/,"Screen message does not indicate block");
400 $sip_patron = C4::SIP::ILS::Patron->new( $patron->cardnumber );
402 is( $sip_patron->fines_amount, 11, "Personal fines correctly reported");
403 ok( $sip_patron->charge_ok, "Patron not blocked");
404 unlike( $sip_patron->screen_msg, qr/Patron blocked by fines .* on related accounts/,"Screen message does not indicate block");
406 $schema->storage->txn_rollback;