Bug 17600: Standardize our EXPORT_OK
[koha.git] / t / db_dependent / Letters.t
1 #!/usr/bin/perl
2
3 # This file is part of Koha.
4 #
5 # Copyright (C) 2013 Equinox Software, Inc.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use Modern::Perl;
21 use Test::More tests => 82;
22 use Test::MockModule;
23 use Test::Warn;
24 use Test::Exception;
25
26 use Email::Sender::Failure;
27
28 use MARC::Record;
29
30 use utf8;
31
32 my ( $email_object, $sendmail_params );
33
34 my $email_sender_module = Test::MockModule->new('Email::Stuffer');
35 $email_sender_module->mock(
36     'send_or_die',
37     sub {
38         ( $email_object, $sendmail_params ) = @_;
39         my $str = $email_object->email->as_string;
40         unlike $str, qr/I =C3=A2=C2=99=C2=A5 Koha=/, "Content is not double encoded";
41         warn "Fake send_or_die";
42     }
43 );
44
45 use_ok('C4::Context');
46 use_ok('C4::Members');
47 use_ok('C4::Acquisition', qw( NewBasket ));
48 use_ok('C4::Biblio', qw( AddBiblio GetBiblioData ));
49 use_ok('C4::Letters', qw( GetMessageTransportTypes GetMessage EnqueueLetter GetQueuedMessages SendQueuedMessages ResendMessage GetLetters GetPreparedLetter SendAlerts ));
50 use t::lib::Mocks;
51 use t::lib::TestBuilder;
52 use Koha::Database;
53 use Koha::DateUtils qw( dt_from_string output_pref );
54 use Koha::Acquisition::Booksellers;
55 use Koha::Acquisition::Bookseller::Contacts;
56 use Koha::Acquisition::Orders;
57 use Koha::Libraries;
58 use Koha::Notice::Templates;
59 use Koha::Patrons;
60 use Koha::Subscriptions;
61 my $schema = Koha::Database->schema;
62 $schema->storage->txn_begin();
63
64 my $builder = t::lib::TestBuilder->new;
65 my $dbh = C4::Context->dbh;
66
67 $dbh->do(q|DELETE FROM letter|);
68 $dbh->do(q|DELETE FROM message_queue|);
69 $dbh->do(q|DELETE FROM message_transport_types|);
70
71 my $library = $builder->build({
72     source => 'Branch',
73     value  => {
74         branchemail      => 'branchemail@address.com',
75         branchreplyto    => 'branchreplyto@address.com',
76         branchreturnpath => 'branchreturnpath@address.com',
77     }
78 });
79 my $patron_category = $builder->build({ source => 'Category' })->{categorycode};
80 my $date = dt_from_string;
81 my $borrowernumber = Koha::Patron->new({
82     firstname    => 'Jane',
83     surname      => 'Smith',
84     categorycode => $patron_category,
85     branchcode   => $library->{branchcode},
86     dateofbirth  => $date,
87     smsalertnumber => undef,
88 })->store->borrowernumber;
89
90 my $marc_record = MARC::Record->new;
91 my( $biblionumber, $biblioitemnumber ) = AddBiblio( $marc_record, '' );
92
93
94
95 # GetMessageTransportTypes
96 my $mtts = C4::Letters::GetMessageTransportTypes();
97 is( @$mtts, 0, 'GetMessageTransportTypes returns the correct number of message types' );
98
99 $dbh->do(q|
100     INSERT INTO message_transport_types( message_transport_type ) VALUES ('email'), ('phone'), ('print'), ('sms')
101 |);
102 $mtts = C4::Letters::GetMessageTransportTypes();
103 is_deeply( $mtts, ['email', 'phone', 'print', 'sms'], 'GetMessageTransportTypes returns all values' );
104
105
106 # EnqueueLetter
107 is( C4::Letters::EnqueueLetter(), undef, 'EnqueueLetter without argument returns undef' );
108
109 my $my_message = {
110     borrowernumber         => $borrowernumber,
111     message_transport_type => 'sms',
112     to_address             => undef,
113     from_address           => 'from@example.com',
114 };
115 my $message_id = C4::Letters::EnqueueLetter($my_message);
116 is( $message_id, undef, 'EnqueueLetter without the letter argument returns undef' );
117
118 delete $my_message->{message_transport_type};
119 $my_message->{letter} = {
120     content      => 'I ♥ Koha',
121     title        => '啤酒 is great',
122     metadata     => 'metadata',
123     code         => 'TEST_MESSAGE',
124     content_type => 'text/plain',
125 };
126
127 $message_id = C4::Letters::EnqueueLetter($my_message);
128 is( $message_id, undef, 'EnqueueLetter without the message type argument argument returns undef' );
129
130 $my_message->{message_transport_type} = 'sms';
131 $message_id = C4::Letters::EnqueueLetter($my_message);
132 ok(defined $message_id && $message_id > 0, 'new message successfully queued');
133
134
135 # GetQueuedMessages
136 my $messages = C4::Letters::GetQueuedMessages();
137 is( @$messages, 1, 'GetQueuedMessages without argument returns all the entries' );
138
139 $messages = C4::Letters::GetQueuedMessages({ borrowernumber => $borrowernumber });
140 is( @$messages, 1, 'one message stored for the borrower' );
141 is( $messages->[0]->{message_id}, $message_id, 'EnqueueLetter returns the message id correctly' );
142 is( $messages->[0]->{borrowernumber}, $borrowernumber, 'EnqueueLetter stores the borrower number correctly' );
143 is( $messages->[0]->{subject}, $my_message->{letter}->{title}, 'EnqueueLetter stores the subject correctly' );
144 is( $messages->[0]->{content}, $my_message->{letter}->{content}, 'EnqueueLetter stores the content correctly' );
145 is( $messages->[0]->{message_transport_type}, $my_message->{message_transport_type}, 'EnqueueLetter stores the message type correctly' );
146 is( $messages->[0]->{status}, 'pending', 'EnqueueLetter stores the status pending correctly' );
147 isnt( $messages->[0]->{time_queued}, undef, 'Time queued inserted by default in message_queue table' );
148 is( $messages->[0]->{updated_on}, $messages->[0]->{time_queued}, 'Time status changed equals time queued when created in message_queue table' );
149 is( $messages->[0]->{delivery_note}, '', 'Delivery note for successful message correctly empty');
150
151 # Setting time_queued to something else than now
152 my $yesterday = dt_from_string->subtract( days => 1 );
153 Koha::Notice::Messages->find($messages->[0]->{message_id})->time_queued($yesterday)->store;
154
155 # SendQueuedMessages
156 my $messages_processed = C4::Letters::SendQueuedMessages( { type => 'email' });
157 is($messages_processed, 0, 'No queued messages processed if type limit passed with unused type');
158 $messages_processed = C4::Letters::SendQueuedMessages( { type => 'sms' });
159 is($messages_processed, 1, 'All queued messages processed, found correct number of messages with type limit');
160 $messages = C4::Letters::GetQueuedMessages({ borrowernumber => $borrowernumber });
161 is(
162     $messages->[0]->{status},
163     'failed',
164     'message marked failed if tried to send SMS message for borrower with no smsalertnumber set (bug 11208)'
165 );
166 isnt($messages->[0]->{updated_on}, $messages->[0]->{time_queued}, 'Time status changed differs from time queued when status changes' );
167 is(dt_from_string($messages->[0]->{time_queued}), $yesterday, 'Time queued remaines inmutable' );
168
169 # ResendMessage
170 my $resent = C4::Letters::ResendMessage($messages->[0]->{message_id});
171 my $message = C4::Letters::GetMessage( $messages->[0]->{message_id});
172 is( $resent, 1, 'The message should have been resent' );
173 is($message->{status},'pending', 'ResendMessage sets status to pending correctly (bug 12426)');
174 $resent = C4::Letters::ResendMessage($messages->[0]->{message_id});
175 is( $resent, 0, 'The message should not have been resent again' );
176 $resent = C4::Letters::ResendMessage();
177 is( $resent, undef, 'ResendMessage should return undef if not message_id given' );
178
179 # Delivery notes
180 is($messages->[0]->{delivery_note}, 'Missing SMS number',
181    'Delivery note for no smsalertnumber correctly set');
182
183
184 # GetLetters
185 my $letters = C4::Letters::GetLetters();
186 is( @$letters, 0, 'GetLetters returns the correct number of letters' );
187
188 my $title = q|<<branches.branchname>> - <<status>>|;
189 my $content = q{Dear <<borrowers.firstname>> <<borrowers.surname>>,
190 According to our current records, you have items that are overdue.Your library does not charge late fines, but please return or renew them at the branch below as soon as possible.
191
192 <<branches.branchname>>
193 <<branches.branchaddress1>>
194 URL: <<OPACBaseURL>>
195
196 The following item(s) is/are currently <<status>>:
197
198 <item> <<count>>. <<items.itemcallnumber>>, Barcode: <<items.barcode>> </item>
199
200 Thank-you for your prompt attention to this matter.
201 Don't forget your date of birth: <<borrowers.dateofbirth>>.
202 Look at this wonderful biblio timestamp: <<biblio.timestamp>>.
203 };
204
205 $dbh->do( q|INSERT INTO letter(branchcode,module,code,name,is_html,title,content,message_transport_type) VALUES (?,'my module','my code','my name',1,?,?,'email')|, undef, $library->{branchcode}, $title, $content );
206 $letters = C4::Letters::GetLetters();
207 is( @$letters, 1, 'GetLetters returns the correct number of letters' );
208 is( $letters->[0]->{module}, 'my module', 'GetLetters gets the module correctly' );
209 is( $letters->[0]->{code}, 'my code', 'GetLetters gets the code correctly' );
210 is( $letters->[0]->{name}, 'my name', 'GetLetters gets the name correctly' );
211
212 # GetPreparedLetter
213 t::lib::Mocks::mock_preference('OPACBaseURL', 'http://thisisatest.com');
214 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', '' );
215
216 my $sms_content = 'This is a SMS for an <<status>>';
217 $dbh->do( q|INSERT INTO letter(branchcode,module,code,name,is_html,title,content,message_transport_type) VALUES (?,'my module','my code','my name',1,'my title',?,'sms')|, undef, $library->{branchcode}, $sms_content );
218
219 my $tables = {
220     borrowers => $borrowernumber,
221     branches => $library->{branchcode},
222     biblio => $biblionumber,
223 };
224 my $substitute = {
225     status => 'overdue',
226 };
227 my $repeat = [
228     {
229         itemcallnumber => 'my callnumber1',
230         barcode        => '1234',
231     },
232     {
233         itemcallnumber => 'my callnumber2',
234         barcode        => '5678',
235     },
236 ];
237 my $prepared_letter = GetPreparedLetter((
238     module      => 'my module',
239     branchcode  => $library->{branchcode},
240     letter_code => 'my code',
241     tables      => $tables,
242     substitute  => $substitute,
243     repeat      => $repeat,
244 ));
245 my $retrieved_library = Koha::Libraries->find($library->{branchcode});
246 my $my_title_letter = $retrieved_library->branchname . qq| - $substitute->{status}|;
247 my $biblio_timestamp = dt_from_string( GetBiblioData($biblionumber)->{timestamp} );
248 my $my_content_letter = qq|Dear Jane Smith,
249 According to our current records, you have items that are overdue.Your library does not charge late fines, but please return or renew them at the branch below as soon as possible.
250
251 |.$retrieved_library->branchname.qq|
252 |.$retrieved_library->branchaddress1.qq|
253 URL: http://thisisatest.com
254
255 The following item(s) is/are currently $substitute->{status}:
256
257 <item> 1. $repeat->[0]->{itemcallnumber}, Barcode: $repeat->[0]->{barcode} </item>
258 <item> 2. $repeat->[1]->{itemcallnumber}, Barcode: $repeat->[1]->{barcode} </item>
259
260 Thank-you for your prompt attention to this matter.
261 Don't forget your date of birth: | . output_pref({ dt => $date, dateonly => 1 }) . q|.
262 Look at this wonderful biblio timestamp: | . output_pref({ dt => $biblio_timestamp })  . ".\n";
263
264 is( $prepared_letter->{title}, $my_title_letter, 'GetPreparedLetter returns the title correctly' );
265 is( $prepared_letter->{content}, $my_content_letter, 'GetPreparedLetter returns the content correctly' );
266
267 $prepared_letter = GetPreparedLetter((
268     module                 => 'my module',
269     branchcode             => $library->{branchcode},
270     letter_code            => 'my code',
271     tables                 => $tables,
272     substitute             => $substitute,
273     repeat                 => $repeat,
274     message_transport_type => 'sms',
275 ));
276 $my_content_letter = qq|This is a SMS for an $substitute->{status}|;
277 is( $prepared_letter->{content}, $my_content_letter, 'GetPreparedLetter returns the content correctly' );
278
279 $dbh->do(q{INSERT INTO letter (module, code, name, title, content) VALUES ('test_date','TEST_DATE','Test dates','A title with a timestamp: <<biblio.timestamp>>','This one only contains the date: <<biblio.timestamp | dateonly>>.');});
280 $prepared_letter = GetPreparedLetter((
281     module                 => 'test_date',
282     branchcode             => '',
283     letter_code            => 'test_date',
284     tables                 => $tables,
285     substitute             => $substitute,
286     repeat                 => $repeat,
287 ));
288 is( $prepared_letter->{content}, q|This one only contains the date: | . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 1' );
289
290 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp | dateonly>>.' WHERE code = 'test_date';});
291 $prepared_letter = GetPreparedLetter((
292     module                 => 'test_date',
293     branchcode             => '',
294     letter_code            => 'test_date',
295     tables                 => $tables,
296     substitute             => $substitute,
297     repeat                 => $repeat,
298 ));
299 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 2' );
300
301 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp|dateonly >>.' WHERE code = 'test_date';});
302 $prepared_letter = GetPreparedLetter((
303     module                 => 'test_date',
304     branchcode             => '',
305     letter_code            => 'test_date',
306     tables                 => $tables,
307     substitute             => $substitute,
308     repeat                 => $repeat,
309 ));
310 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 3' );
311
312 t::lib::Mocks::mock_preference( 'TimeFormat', '12hr' );
313 my $yesterday_night = $date->clone->add( days => -1 )->set_hour(22);
314 $dbh->do(q|UPDATE biblio SET timestamp = ? WHERE biblionumber = ?|, undef, $yesterday_night, $biblionumber );
315 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp>>.' WHERE code = 'test_date';});
316 $prepared_letter = GetPreparedLetter((
317     module                 => 'test_date',
318     branchcode             => '',
319     letter_code            => 'test_date',
320     tables                 => $tables,
321     substitute             => $substitute,
322     repeat                 => $repeat,
323 ));
324 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $yesterday_night }) . q|.|, 'dateonly test 3' );
325
326 $dbh->do(q{INSERT INTO letter (module, code, name, title, content) VALUES ('claimacquisition','TESTACQCLAIM','Acquisition Claim','Item Not Received','<<aqbooksellers.name>>|<<aqcontacts.name>>|<order>Ordernumber <<aqorders.ordernumber>> (<<biblio.title>>) (<<aqorders.quantity>> ordered)</order>');});
327 $dbh->do(q{INSERT INTO letter (module, code, name, title, content) VALUES ('orderacquisition','TESTACQORDER','Acquisition Order','Order','<<aqbooksellers.name>>|<<aqcontacts.name>>|<order>Ordernumber <<aqorders.ordernumber>> (<<biblio.title>>) (<<aqorders.quantity>> ordered)</order>');});
328
329 # Test that _parseletter doesn't modify its parameters bug 15429
330 {
331     my $values = { dateexpiry => '2015-12-13', };
332     C4::Letters::_parseletter($prepared_letter, 'borrowers', $values);
333     is( $values->{dateexpiry}, '2015-12-13', "_parseletter doesn't modify its parameters" );
334 }
335
336 # Correctly format dateexpiry
337 {
338     my $values = { dateexpiry => '2015-12-13', };
339
340     t::lib::Mocks::mock_preference('dateformat', 'metric');
341     t::lib::Mocks::mock_preference('timeformat', '24hr');
342     my $letter = C4::Letters::_parseletter({ content => "expiry on <<borrowers.dateexpiry>>"}, 'borrowers', $values);
343     is( $letter->{content}, 'expiry on 13/12/2015' );
344
345     t::lib::Mocks::mock_preference('dateformat', 'metric');
346     t::lib::Mocks::mock_preference('timeformat', '12hr');
347     $letter = C4::Letters::_parseletter({ content => "expiry on <<borrowers.dateexpiry>>"}, 'borrowers', $values);
348     is( $letter->{content}, 'expiry on 13/12/2015' );
349 }
350
351 my $bookseller = Koha::Acquisition::Bookseller->new(
352     {
353         name => "my vendor",
354         address1 => "bookseller's address",
355         phone => "0123456",
356         active => 1,
357         deliverytime => 5,
358     }
359 )->store;
360 my $booksellerid = $bookseller->id;
361
362 Koha::Acquisition::Bookseller::Contact->new( { name => 'John Smith',  phone => '0123456x1', claimacquisition => 1, orderacquisition => 1, booksellerid => $booksellerid } )->store;
363 Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues      => 1, booksellerid => $booksellerid } )->store;
364 my $basketno = NewBasket($booksellerid, 1);
365
366 my $budgetid = C4::Budgets::AddBudget({
367     budget_code => "budget_code_test_letters",
368     budget_name => "budget_name_test_letters",
369 });
370
371 my $bib = MARC::Record->new();
372 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
373     $bib->append_fields(
374         MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
375     );
376 } else {
377     $bib->append_fields(
378         MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
379     );
380 }
381
382 my $logged_in_user = $builder->build_object(
383     {
384         class => 'Koha::Patrons',
385         value => {
386             branchcode => $library->{branchcode},
387             email      => 'some@email.com'
388         }
389     }
390 );
391
392 t::lib::Mocks::mock_userenv({ patron => $logged_in_user });
393
394 ($biblionumber, $biblioitemnumber) = AddBiblio($bib, '');
395 my $order = Koha::Acquisition::Order->new(
396     {
397         basketno => $basketno,
398         quantity => 1,
399         biblionumber => $biblionumber,
400         budget_id => $budgetid,
401     }
402 )->store;
403 my $ordernumber = $order->ordernumber;
404
405 Koha::Acquisition::Baskets->find( $basketno )->close;
406 my $err;
407 warning_like {
408     $err = SendAlerts( 'claimacquisition', [ $ordernumber ], 'TESTACQCLAIM' ) }
409     qr/^Bookseller .* without emails at/,
410     "SendAlerts prints a warning";
411 is($err->{'error'}, 'no_email', "Trying to send an alert when there's no e-mail results in an error");
412
413 $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
414 $bookseller->contacts->next->email('testemail@mydomain.com')->store;
415
416 # Ensure that the preference 'ClaimsLog' is set to logging
417 t::lib::Mocks::mock_preference( 'ClaimsLog', 'on' );
418
419 # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
420 t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
421
422 {
423 warning_like {
424     $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ) }
425     qr|Fake send_or_die|,
426     "SendAlerts is using the mocked send_or_die routine (orderacquisition)";
427 is($err, 1, "Successfully sent order.");
428 is($email_object->email->header('To'), 'testemail@mydomain.com', "mailto correct in sent order");
429 is($email_object->email->body, 'my vendor|John Smith|Ordernumber ' . $ordernumber . ' (Silence in the library) (1 ordered)', 'Order notice text constructed successfully');
430
431 my $mocked_koha_email = Test::MockModule->new('Koha::Email');
432 $mocked_koha_email->mock( 'send_or_die', sub {
433     Email::Sender::Failure->throw('something went wrong');
434 });
435
436 warning_like {
437     $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ); }
438     qr{something went wrong},
439     'Warning is printed';
440
441 is($err->{error}, 'something went wrong', "Send exception, error message returned");
442
443 $dbh->do(q{DELETE FROM letter WHERE code = 'TESTACQORDER';});
444 warning_like {
445     $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ) }
446     qr/No orderacquisition TESTACQORDER letter transported by email/,
447     "GetPreparedLetter warns about missing notice template";
448 is($err->{'error'}, 'no_letter', "No TESTACQORDER letter was defined.");
449 }
450
451 {
452 warning_like {
453     $err = SendAlerts( 'claimacquisition', [ $ordernumber ], 'TESTACQCLAIM' ) }
454     qr|Fake send_or_die|,
455     "SendAlerts is using the mocked send_or_die routine";
456
457 is($err, 1, "Successfully sent claim");
458 is($email_object->email->header('To'), 'testemail@mydomain.com', "mailto correct in sent claim");
459 is($email_object->email->body, 'my vendor|John Smith|Ordernumber ' . $ordernumber . ' (Silence in the library) (1 ordered)', 'Claim notice text constructed successfully');
460
461 my $mocked_koha_email = Test::MockModule->new('Koha::Email');
462 $mocked_koha_email->mock( 'send_or_die', sub {
463     Email::Sender::Failure->throw('something went wrong');
464 });
465
466 warning_like {
467     $err = SendAlerts( 'claimacquisition', [ $ordernumber ] , 'TESTACQCLAIM' ); }
468     qr{something went wrong},
469     'Warning is printed';
470
471 is($err->{error}, 'something went wrong', "Send exception, error message returned");
472 }
473
474 {
475 use C4::Serials qw( NewSubscription GetSerials findSerialsByStatus ModSerialStatus );
476
477 my $notes = 'notes';
478 my $internalnotes = 'intnotes';
479 $dbh->do(q|UPDATE subscription_numberpatterns SET numberingmethod='No. {X}' WHERE id=1|);
480 my $subscriptionid = NewSubscription(
481      undef,      "",     undef, undef, undef, $biblionumber,
482     '2013-01-01', 1, undef, undef,  undef,
483     undef,      undef,  undef, undef, undef, undef,
484     1,          $notes,undef, '2013-01-01', undef, 1,
485     undef,       undef,  0,    $internalnotes,  0,
486     undef, undef, 0,          undef,         '2013-12-31', 0
487 );
488 $dbh->do(q{INSERT INTO letter (module, code, name, title, content) VALUES ('serial','RLIST','Serial issue notification','Serial issue notification','<<biblio.title>>,<<subscription.subscriptionid>>,<<serial.serialseq>>');});
489 my ($serials_count, @serials) = GetSerials($subscriptionid);
490 my $serial = $serials[0];
491
492 my $patron = Koha::Patron->new({
493     firstname    => 'John',
494     surname      => 'Smith',
495     categorycode => $patron_category,
496     branchcode   => $library->{branchcode},
497     dateofbirth  => $date,
498     email        => 'john.smith@test.de',
499 })->store;
500 my $borrowernumber = $patron->borrowernumber;
501 my $subscription = Koha::Subscriptions->find( $subscriptionid );
502 $subscription->add_subscriber( $patron );
503
504 t::lib::Mocks::mock_userenv({ branch => $library->{branchcode} });
505 my $err2;
506 warning_like {
507 $err2 = SendAlerts( 'issue', $serial->{serialid}, 'RLIST' ) }
508     qr|Fake send_or_die|,
509     "SendAlerts is using the mocked send_or_die routine";
510
511 is($err2, 1, "Successfully sent serial notification");
512 is($email_object->email->header('To'), 'john.smith@test.de', "mailto correct in sent serial notification");
513 is($email_object->email->body, 'Silence in the library,'.$subscriptionid.',No. 0', 'Serial notification text constructed successfully');
514
515 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', 'robert.tables@mail.com' );
516
517 my $err3;
518 warning_like {
519 $err3 = SendAlerts( 'issue', $serial->{serialid}, 'RLIST' ) }
520     qr|Fake send_or_die|,
521     "SendAlerts is using the mocked send_or_die routine";
522 is($email_object->email->header('To'), 'robert.tables@mail.com', "mailto address overwritten by SendAllMailsTo preference");
523
524 my $mocked_koha_email = Test::MockModule->new('Koha::Email');
525 $mocked_koha_email->mock( 'send_or_die', sub {
526     Email::Sender::Failure->throw('something went wrong');
527 });
528
529 warning_like {
530     $err = SendAlerts( 'issue', $serial->{serialid} , 'RLIST' ); }
531     qr{something went wrong},
532     'Warning is printed';
533
534 is($err->{error}, 'something went wrong', "Send exception, error message returned");
535
536 }
537 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', '' );
538
539 subtest 'SendAlerts - claimissue' => sub {
540     plan tests => 13;
541
542     use C4::Serials;
543
544     $dbh->do(q{INSERT INTO letter (module, code, name, title, content) VALUES ('claimissues','TESTSERIALCLAIM','Serial claim test','Serial claim test','<<serial.serialid>>|<<subscription.startdate>>|<<biblio.title>>|<<biblioitems.issn>>');});
545
546     my $bookseller = Koha::Acquisition::Bookseller->new(
547         {
548             name => "my vendor",
549             address1 => "bookseller's address",
550             phone => "0123456",
551             active => 1,
552             deliverytime => 5,
553         }
554     )->store;
555     my $booksellerid = $bookseller->id;
556
557     Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues => 1, booksellerid => $booksellerid } )->store;
558
559     my $bib = MARC::Record->new();
560     if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
561         $bib->append_fields(
562             MARC::Field->new('011', ' ', ' ', a => 'xxxx-yyyy'),
563             MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
564         );
565     } else {
566         $bib->append_fields(
567             MARC::Field->new('022', ' ', ' ', a => 'xxxx-yyyy'),
568             MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
569         );
570     }
571     my ($biblionumber) = AddBiblio($bib, '');
572
573     $dbh->do(q|UPDATE subscription_numberpatterns SET numberingmethod='No. {X}' WHERE id=1|);
574     my $subscriptionid = NewSubscription(
575          undef, "", $booksellerid, undef, undef, $biblionumber,
576         '2013-01-01', 1, undef, undef,  undef,
577         undef,  undef,  undef, undef, undef, undef,
578         1, 'public',undef, '2013-01-01', undef, 1,
579         undef, undef,  0, 'internal',  0,
580         undef, undef, 0,  undef, '2013-12-31', 0
581     );
582
583     my ($serials_count, @serials) = GetSerials($subscriptionid);
584     my  @serialids = ($serials[0]->{serialid});
585
586     my $err;
587     warning_like {
588         $err = SendAlerts( 'claimissues', \@serialids, 'TESTSERIALCLAIM' ) }
589         qr/^Bookseller .* without emails at/,
590         "Warn on vendor without email address";
591
592     $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
593     $bookseller->contacts->next->email('testemail@mydomain.com')->store;
594
595     # Ensure that the preference 'ClaimsLog' is set to logging
596     t::lib::Mocks::mock_preference( 'ClaimsLog', 'on' );
597
598     # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
599     t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
600
601     t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
602
603     {
604     warning_like {
605         $err = SendAlerts( 'claimissues', \@serialids , 'TESTSERIALCLAIM' ) }
606         qr|Fake send_or_die|,
607         "SendAlerts is using the mocked send_or_die routine (claimissues)";
608     is( $err, 1, "Successfully sent claim" );
609     is( $email_object->email->header('To'),
610         'testemail@mydomain.com', "mailto correct in sent claim" );
611     is(
612         $email_object->email->body,
613         "$serialids[0]|2013-01-01|Silence in the library|xxxx-yyyy",
614         'Serial claim letter for 1 issue constructed successfully'
615     );
616
617     my $mocked_koha_email = Test::MockModule->new('Koha::Email');
618     $mocked_koha_email->mock( 'send_or_die', sub {
619             Email::Sender::Failure->throw('something went wrong');
620     });
621
622     warning_like {
623         $err = SendAlerts( 'claimissues', \@serialids , 'TESTSERIALCLAIM' ); }
624         qr{something went wrong},
625         'Warning is printed';
626
627     is($err->{error}, 'something went wrong', "Send exception, error message returned");
628     }
629
630     {
631     my $publisheddate = output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 });
632     my $serialexpected = ( C4::Serials::findSerialsByStatus( 1, $subscriptionid ) )[0];
633     ModSerialStatus( $serials[0]->{serialid}, "No. 1", $publisheddate, $publisheddate, $publisheddate, '3', 'a note' );
634     ($serials_count, @serials) = GetSerials($subscriptionid);
635     push @serialids, ($serials[1]->{serialid});
636
637     warning_like { $err = SendAlerts( 'claimissues', \@serialids, 'TESTSERIALCLAIM' ); }
638         qr|Fake send_or_die|,
639         "SendAlerts is using the mocked send_or_die routine (claimissues)";
640
641     is(
642         $email_object->email->body,
643         "$serialids[0]|2013-01-01|Silence in the library|xxxx-yyyy"
644           . $email_object->email->crlf
645           . "$serialids[1]|2013-01-01|Silence in the library|xxxx-yyyy",
646         "Serial claim letter for 2 issues constructed successfully"
647     );
648
649     $dbh->do(q{DELETE FROM letter WHERE code = 'TESTSERIALCLAIM';});
650     warning_like {
651         $err = SendAlerts( 'orderacquisition', $basketno , 'TESTSERIALCLAIM' ) }
652         qr/No orderacquisition TESTSERIALCLAIM letter transported by email/,
653         "GetPreparedLetter warns about missing notice template";
654     is($err->{'error'}, 'no_letter', "No TESTSERIALCLAIM letter was defined");
655     }
656
657 };
658
659 subtest 'GetPreparedLetter' => sub {
660     plan tests => 4;
661
662     Koha::Notice::Template->new(
663         {
664             module                 => 'test',
665             code                   => 'test',
666             branchcode             => '',
667             message_transport_type => 'email'
668         }
669     )->store;
670     my $letter;
671     warning_like {
672         $letter = C4::Letters::GetPreparedLetter(
673             module      => 'test',
674             letter_code => 'test',
675         );
676     }
677     qr{^ERROR: nothing to substitute},
678 'GetPreparedLetter should warn if tables, substiture and repeat are not set';
679     is( $letter, undef,
680 'No letter should be returned by GetPreparedLetter if something went wrong'
681     );
682
683     warning_like {
684         $letter = C4::Letters::GetPreparedLetter(
685             module      => 'test',
686             letter_code => 'test',
687             substitute  => {}
688         );
689     }
690     qr{^ERROR: nothing to substitute},
691 'GetPreparedLetter should warn if tables, substiture and repeat are not set, even if the key is passed';
692     is( $letter, undef,
693 'No letter should be returned by GetPreparedLetter if something went wrong'
694     );
695
696 };
697
698
699
700 subtest 'TranslateNotices' => sub {
701     plan tests => 4;
702
703     t::lib::Mocks::mock_preference( 'TranslateNotices', '1' );
704
705     $dbh->do(
706         q|
707         INSERT INTO letter (module, code, branchcode, name, title, content, message_transport_type, lang) VALUES
708         ('test', 'code', '', 'test', 'a test', 'just a test', 'email', 'default'),
709         ('test', 'code', '', 'test', 'una prueba', 'solo una prueba', 'email', 'es-ES');
710     | );
711     my $substitute = {};
712     my $letter = C4::Letters::GetPreparedLetter(
713             module                 => 'test',
714             tables                 => $tables,
715             letter_code            => 'code',
716             message_transport_type => 'email',
717             substitute             => $substitute,
718     );
719     is(
720         $letter->{title},
721         'a test',
722         'GetPreparedLetter should return the default one if the lang parameter is not provided'
723     );
724
725     $letter = C4::Letters::GetPreparedLetter(
726             module                 => 'test',
727             tables                 => $tables,
728             letter_code            => 'code',
729             message_transport_type => 'email',
730             substitute             => $substitute,
731             lang                   => 'es-ES',
732     );
733     is( $letter->{title}, 'una prueba',
734         'GetPreparedLetter should return the required notice if it exists' );
735
736     $letter = C4::Letters::GetPreparedLetter(
737             module                 => 'test',
738             tables                 => $tables,
739             letter_code            => 'code',
740             message_transport_type => 'email',
741             substitute             => $substitute,
742             lang                   => 'fr-FR',
743     );
744     is(
745         $letter->{title},
746         'a test',
747         'GetPreparedLetter should return the default notice if the one required does not exist'
748     );
749
750     t::lib::Mocks::mock_preference( 'TranslateNotices', '' );
751
752     $letter = C4::Letters::GetPreparedLetter(
753             module                 => 'test',
754             tables                 => $tables,
755             letter_code            => 'code',
756             message_transport_type => 'email',
757             substitute             => $substitute,
758             lang                   => 'es-ES',
759     );
760     is( $letter->{title}, 'a test',
761         'GetPreparedLetter should return the default notice if pref disabled but additional language exists' );
762
763 };
764
765 subtest 'SendQueuedMessages' => sub {
766
767     plan tests => 12;
768
769     t::lib::Mocks::mock_preference( 'SMSSendDriver', 'Email' );
770     t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', '');
771
772     my $patron = Koha::Patrons->find($borrowernumber);
773     $dbh->do(q|
774         INSERT INTO message_queue(borrowernumber, subject, content, message_transport_type, status, letter_code)
775         VALUES (?, 'subject', 'content', 'sms', 'pending', 'just_a_code')
776         |, undef, $borrowernumber
777     );
778     eval { C4::Letters::SendQueuedMessages(); };
779     is( $@, '', 'SendQueuedMessages should not explode if the patron does not have a sms provider set' );
780
781     my $sms_pro = $builder->build_object({ class => 'Koha::SMS::Providers', value => { domain => 'kidclamp.rocks' } });
782     $patron->set( { smsalertnumber => '5555555555', sms_provider_id => $sms_pro->id() } )->store;
783     $message_id = C4::Letters::EnqueueLetter($my_message); #using datas set around line 95 and forward
784
785     warning_like { C4::Letters::SendQueuedMessages(); }
786         qr|Fake send_or_die|,
787         "SendAlerts is using the mocked send_or_die routine (claimissues)";
788
789     my $message = $schema->resultset('MessageQueue')->search({
790         borrowernumber => $borrowernumber,
791         status => 'sent'
792     })->next();
793
794     is( $message->to_address(), '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address not set' );
795     is(
796         $message->from_address(),
797         'from@example.com',
798         'SendQueuedMessages uses message queue item \"from address\" for SMS by email when EmailSMSSendDriverFromAddress system preference is not set'
799     );
800
801     $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber, status => 'sent'})->delete(); #clear borrower queue
802
803     t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', 'override@example.com');
804
805     $message_id = C4::Letters::EnqueueLetter($my_message);
806     warning_like { C4::Letters::SendQueuedMessages(); }
807         qr|Fake send_or_die|,
808         "SendAlerts is using the mocked send_or_die routine (claimissues)";
809
810     $message = $schema->resultset('MessageQueue')->search({
811         borrowernumber => $borrowernumber,
812         status => 'sent'
813     })->next();
814
815     is(
816         $message->from_address(),
817         'override@example.com',
818         'SendQueuedMessages uses EmailSMSSendDriverFromAddress value for SMS by email when EmailSMSSendDriverFromAddress is set'
819     );
820
821     $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber,status => 'sent'})->delete(); #clear borrower queue
822     $my_message->{to_address} = 'fixme@kidclamp.iswrong';
823     $message_id = C4::Letters::EnqueueLetter($my_message);
824
825     my $number_attempted = C4::Letters::SendQueuedMessages({
826         borrowernumber => -1, # -1 still triggers the borrowernumber condition
827         letter_code    => 'PASSWORD_RESET',
828     });
829     is ( $number_attempted, 0, 'There were no password reset messages for SendQueuedMessages to attempt.' );
830
831     warning_like { C4::Letters::SendQueuedMessages(); }
832         qr|Fake send_or_die|,
833         "SendAlerts is using the mocked send_or_die routine (claimissues)";
834
835     my $sms_message_address = $schema->resultset('MessageQueue')->search({
836         borrowernumber => $borrowernumber,
837         status => 'sent'
838     })->next()->to_address();
839     is( $sms_message_address, '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address is set incorrectly' );
840
841 };
842
843 subtest 'get_item_content' => sub {
844     plan tests => 2;
845
846     t::lib::Mocks::mock_preference('dateformat', 'metric');
847     t::lib::Mocks::mock_preference('timeformat', '24hr');
848     my @items = (
849         {date_due => '2041-01-01 12:34', title => 'a first title', barcode => 'a_first_barcode', author => 'a_first_author', itemnumber => 1 },
850         {date_due => '2042-01-02 23:45', title => 'a second title', barcode => 'a_second_barcode', author => 'a_second_author', itemnumber => 2 },
851     );
852     my @item_content_fields = qw( date_due title barcode author itemnumber );
853
854     my $items_content;
855     for my $item ( @items ) {
856         $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields } );
857     }
858
859     my $expected_items_content = <<EOF;
860 01/01/2041 12:34\ta first title\ta_first_barcode\ta_first_author\t1
861 02/01/2042 23:45\ta second title\ta_second_barcode\ta_second_author\t2
862 EOF
863     is( $items_content, $expected_items_content, 'get_item_content should return correct items info with time (default)' );
864
865
866     $items_content = q||;
867     for my $item ( @items ) {
868         $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields, dateonly => 1, } );
869     }
870
871     $expected_items_content = <<EOF;
872 01/01/2041\ta first title\ta_first_barcode\ta_first_author\t1
873 02/01/2042\ta second title\ta_second_barcode\ta_second_author\t2
874 EOF
875     is( $items_content, $expected_items_content, 'get_item_content should return correct items info without time (if dateonly => 1)' );
876 };
877
878 subtest 'Test limit parameter for SendQueuedMessages' => sub {
879     plan tests => 3;
880
881     my $dbh = C4::Context->dbh;
882
883     my $borrowernumber = Koha::Patron->new({
884         firstname    => 'Jane',
885         surname      => 'Smith',
886         categorycode => $patron_category,
887         branchcode   => $library->{branchcode},
888         dateofbirth  => $date,
889         smsalertnumber => undef,
890     })->store->borrowernumber;
891
892     $dbh->do(q|DELETE FROM message_queue|);
893     $my_message = {
894         'letter' => {
895             'content'      => 'a message',
896             'metadata'     => 'metadata',
897             'code'         => 'TEST_MESSAGE',
898             'content_type' => 'text/plain',
899             'title'        => 'message title'
900         },
901         'borrowernumber'         => $borrowernumber,
902         'to_address'             => undef,
903         'message_transport_type' => 'sms',
904         'from_address'           => 'from@example.com'
905     };
906     C4::Letters::EnqueueLetter($my_message);
907     C4::Letters::EnqueueLetter($my_message);
908     C4::Letters::EnqueueLetter($my_message);
909     C4::Letters::EnqueueLetter($my_message);
910     C4::Letters::EnqueueLetter($my_message);
911     my $messages_processed = C4::Letters::SendQueuedMessages( { limit => 1 } );
912     is( $messages_processed, 1,
913         'Processed 1 message with limit of 1 and 5 unprocessed messages' );
914     $messages_processed = C4::Letters::SendQueuedMessages( { limit => 2 } );
915     is( $messages_processed, 2,
916         'Processed 2 message with limit of 2 and 4 unprocessed messages' );
917     $messages_processed = C4::Letters::SendQueuedMessages( { limit => 3 } );
918     is( $messages_processed, 2,
919         'Processed 2 message with limit of 3 and 2 unprocessed messages' );
920 };
921
922 subtest 'Test message_id parameter for SendQueuedMessages' => sub {
923
924     plan tests => 6;
925
926     my $dbh = C4::Context->dbh;
927
928     my $borrowernumber = Koha::Patron->new({
929         firstname    => 'Jane',
930         surname      => 'Smith',
931         categorycode => $patron_category,
932         branchcode   => $library->{branchcode},
933         dateofbirth  => $date,
934         smsalertnumber => undef,
935     })->store->borrowernumber;
936
937     $dbh->do(q|DELETE FROM message_queue|);
938     $my_message = {
939         'letter' => {
940             'content'      => 'a message',
941             'metadata'     => 'metadata',
942             'code'         => 'TEST_MESSAGE',
943             'content_type' => 'text/plain',
944             'title'        => 'message title'
945         },
946         'borrowernumber'         => $borrowernumber,
947         'to_address'             => 'to@example.org',
948         'message_transport_type' => 'email',
949         'from_address'           => 'root@localhost.' # invalid KohaAdminEmailAddress
950     };
951     my $message_id = C4::Letters::EnqueueLetter($my_message);
952     throws_ok {
953         C4::Letters::SendQueuedMessages( { message_id => $message_id } );
954     } 'Koha::Exceptions::BadParameter',
955     'Exception thrown if invalid email is passed';
956     my $message_1 = C4::Letters::GetMessage($message_id);
957     # FIXME must be 'failed'
958     is( $message_1->{status}, 'pending', 'Invalid KohaAdminEmailAddress => status pending' );
959
960     $my_message->{from_address} = 'root@example.org'; # valid KohaAdminEmailAddress
961     $message_id = C4::Letters::EnqueueLetter($my_message);
962     warning_like { C4::Letters::SendQueuedMessages( { message_id => $message_id } ); }
963         qr|Fake send_or_die|,
964         "SendQueuedMessages is using the mocked send_or_die routine";
965     $message_1 = C4::Letters::GetMessage($message_1->{message_id});
966     my $message_2 = C4::Letters::GetMessage($message_id);
967     is( $message_1->{status}, 'pending', 'Message 1 status is unchanged' ); # Must be 'failed'
968     is( $message_2->{status}, 'sent', 'Valid KohaAdminEmailAddress => status sent' );
969 };