3 # This file is part of Koha.
5 # Copyright (C) 2013 Equinox Software, Inc.
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.
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.
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>.
21 use Test::More tests => 82;
26 use Email::Sender::Failure;
32 my ( $email_object, $sendmail_params );
34 my $email_sender_module = Test::MockModule->new('Email::Stuffer');
35 $email_sender_module->mock(
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";
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 ));
51 use t::lib::TestBuilder;
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;
58 use Koha::Notice::Templates;
60 use Koha::Subscriptions;
61 my $schema = Koha::Database->schema;
62 $schema->storage->txn_begin();
64 my $builder = t::lib::TestBuilder->new;
65 my $dbh = C4::Context->dbh;
67 $dbh->do(q|DELETE FROM letter|);
68 $dbh->do(q|DELETE FROM message_queue|);
69 $dbh->do(q|DELETE FROM message_transport_types|);
71 my $library = $builder->build({
74 branchemail => 'branchemail@address.com',
75 branchreplyto => 'branchreplyto@address.com',
76 branchreturnpath => 'branchreturnpath@address.com',
79 my $patron_category = $builder->build({ source => 'Category' })->{categorycode};
80 my $date = dt_from_string;
81 my $borrowernumber = Koha::Patron->new({
84 categorycode => $patron_category,
85 branchcode => $library->{branchcode},
87 smsalertnumber => undef,
88 })->store->borrowernumber;
90 my $marc_record = MARC::Record->new;
91 my( $biblionumber, $biblioitemnumber ) = AddBiblio( $marc_record, '' );
95 # GetMessageTransportTypes
96 my $mtts = C4::Letters::GetMessageTransportTypes();
97 is( @$mtts, 0, 'GetMessageTransportTypes returns the correct number of message types' );
100 INSERT INTO message_transport_types( message_transport_type ) VALUES ('email'), ('phone'), ('print'), ('sms')
102 $mtts = C4::Letters::GetMessageTransportTypes();
103 is_deeply( $mtts, ['email', 'phone', 'print', 'sms'], 'GetMessageTransportTypes returns all values' );
107 is( C4::Letters::EnqueueLetter(), undef, 'EnqueueLetter without argument returns undef' );
110 borrowernumber => $borrowernumber,
111 message_transport_type => 'sms',
113 from_address => 'from@example.com',
115 my $message_id = C4::Letters::EnqueueLetter($my_message);
116 is( $message_id, undef, 'EnqueueLetter without the letter argument returns undef' );
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',
127 $message_id = C4::Letters::EnqueueLetter($my_message);
128 is( $message_id, undef, 'EnqueueLetter without the message type argument argument returns undef' );
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');
136 my $messages = C4::Letters::GetQueuedMessages();
137 is( @$messages, 1, 'GetQueuedMessages without argument returns all the entries' );
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');
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;
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 });
162 $messages->[0]->{status},
164 'message marked failed if tried to send SMS message for borrower with no smsalertnumber set (bug 11208)'
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' );
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' );
180 is($messages->[0]->{delivery_note}, 'Missing SMS number',
181 'Delivery note for no smsalertnumber correctly set');
185 my $letters = C4::Letters::GetLetters();
186 is( @$letters, 0, 'GetLetters returns the correct number of letters' );
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.
192 <<branches.branchname>>
193 <<branches.branchaddress1>>
196 The following item(s) is/are currently <<status>>:
198 <item> <<count>>. <<items.itemcallnumber>>, Barcode: <<items.barcode>> </item>
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>>.
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' );
213 t::lib::Mocks::mock_preference('OPACBaseURL', 'http://thisisatest.com');
214 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', '' );
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 );
220 borrowers => $borrowernumber,
221 branches => $library->{branchcode},
222 biblio => $biblionumber,
229 itemcallnumber => 'my callnumber1',
233 itemcallnumber => 'my callnumber2',
237 my $prepared_letter = GetPreparedLetter((
238 module => 'my module',
239 branchcode => $library->{branchcode},
240 letter_code => 'my code',
242 substitute => $substitute,
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.
251 |.$retrieved_library->branchname.qq|
252 |.$retrieved_library->branchaddress1.qq|
253 URL: http://thisisatest.com
255 The following item(s) is/are currently $substitute->{status}:
257 <item> 1. $repeat->[0]->{itemcallnumber}, Barcode: $repeat->[0]->{barcode} </item>
258 <item> 2. $repeat->[1]->{itemcallnumber}, Barcode: $repeat->[1]->{barcode} </item>
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";
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' );
267 $prepared_letter = GetPreparedLetter((
268 module => 'my module',
269 branchcode => $library->{branchcode},
270 letter_code => 'my code',
272 substitute => $substitute,
274 message_transport_type => 'sms',
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' );
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',
283 letter_code => 'test_date',
285 substitute => $substitute,
288 is( $prepared_letter->{content}, q|This one only contains the date: | . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 1' );
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',
294 letter_code => 'test_date',
296 substitute => $substitute,
299 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 2' );
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',
305 letter_code => 'test_date',
307 substitute => $substitute,
310 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 3' );
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',
319 letter_code => 'test_date',
321 substitute => $substitute,
324 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $yesterday_night }) . q|.|, 'dateonly test 3' );
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>');});
329 # Test that _parseletter doesn't modify its parameters bug 15429
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" );
336 # Correctly format dateexpiry
338 my $values = { dateexpiry => '2015-12-13', };
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' );
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' );
351 my $bookseller = Koha::Acquisition::Bookseller->new(
354 address1 => "bookseller's address",
360 my $booksellerid = $bookseller->id;
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);
366 my $budgetid = C4::Budgets::AddBudget({
367 budget_code => "budget_code_test_letters",
368 budget_name => "budget_name_test_letters",
371 my $bib = MARC::Record->new();
372 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
374 MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
378 MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
382 my $logged_in_user = $builder->build_object(
384 class => 'Koha::Patrons',
386 branchcode => $library->{branchcode},
387 email => 'some@email.com'
392 t::lib::Mocks::mock_userenv({ patron => $logged_in_user });
394 ($biblionumber, $biblioitemnumber) = AddBiblio($bib, '');
395 my $order = Koha::Acquisition::Order->new(
397 basketno => $basketno,
399 biblionumber => $biblionumber,
400 budget_id => $budgetid,
403 my $ordernumber = $order->ordernumber;
405 Koha::Acquisition::Baskets->find( $basketno )->close;
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");
413 $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
414 $bookseller->contacts->next->email('testemail@mydomain.com')->store;
416 # Ensure that the preference 'ClaimsLog' is set to logging
417 t::lib::Mocks::mock_preference( 'ClaimsLog', 'on' );
419 # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
420 t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
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');
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');
437 $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ); }
438 qr{something went wrong},
439 'Warning is printed';
441 is($err->{error}, 'something went wrong', "Send exception, error message returned");
443 $dbh->do(q{DELETE FROM letter WHERE code = 'TESTACQORDER';});
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.");
453 $err = SendAlerts( 'claimacquisition', [ $ordernumber ], 'TESTACQCLAIM' ) }
454 qr|Fake send_or_die|,
455 "SendAlerts is using the mocked send_or_die routine";
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');
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');
467 $err = SendAlerts( 'claimacquisition', [ $ordernumber ] , 'TESTACQCLAIM' ); }
468 qr{something went wrong},
469 'Warning is printed';
471 is($err->{error}, 'something went wrong', "Send exception, error message returned");
475 use C4::Serials qw( NewSubscription GetSerials findSerialsByStatus ModSerialStatus );
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
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];
492 my $patron = Koha::Patron->new({
495 categorycode => $patron_category,
496 branchcode => $library->{branchcode},
497 dateofbirth => $date,
498 email => 'john.smith@test.de',
500 my $borrowernumber = $patron->borrowernumber;
501 my $subscription = Koha::Subscriptions->find( $subscriptionid );
502 $subscription->add_subscriber( $patron );
504 t::lib::Mocks::mock_userenv({ branch => $library->{branchcode} });
507 $err2 = SendAlerts( 'issue', $serial->{serialid}, 'RLIST' ) }
508 qr|Fake send_or_die|,
509 "SendAlerts is using the mocked send_or_die routine";
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');
515 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', 'robert.tables@mail.com' );
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");
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');
530 $err = SendAlerts( 'issue', $serial->{serialid} , 'RLIST' ); }
531 qr{something went wrong},
532 'Warning is printed';
534 is($err->{error}, 'something went wrong', "Send exception, error message returned");
537 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', '' );
539 subtest 'SendAlerts - claimissue' => sub {
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>>');});
546 my $bookseller = Koha::Acquisition::Bookseller->new(
549 address1 => "bookseller's address",
555 my $booksellerid = $bookseller->id;
557 Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues => 1, booksellerid => $booksellerid } )->store;
559 my $bib = MARC::Record->new();
560 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
562 MARC::Field->new('011', ' ', ' ', a => 'xxxx-yyyy'),
563 MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
567 MARC::Field->new('022', ' ', ' ', a => 'xxxx-yyyy'),
568 MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
571 my ($biblionumber) = AddBiblio($bib, '');
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
583 my ($serials_count, @serials) = GetSerials($subscriptionid);
584 my @serialids = ($serials[0]->{serialid});
588 $err = SendAlerts( 'claimissues', \@serialids, 'TESTSERIALCLAIM' ) }
589 qr/^Bookseller .* without emails at/,
590 "Warn on vendor without email address";
592 $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
593 $bookseller->contacts->next->email('testemail@mydomain.com')->store;
595 # Ensure that the preference 'ClaimsLog' is set to logging
596 t::lib::Mocks::mock_preference( 'ClaimsLog', 'on' );
598 # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
599 t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
601 t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
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" );
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'
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');
623 $err = SendAlerts( 'claimissues', \@serialids , 'TESTSERIALCLAIM' ); }
624 qr{something went wrong},
625 'Warning is printed';
627 is($err->{error}, 'something went wrong', "Send exception, error message returned");
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});
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)";
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"
649 $dbh->do(q{DELETE FROM letter WHERE code = 'TESTSERIALCLAIM';});
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");
659 subtest 'GetPreparedLetter' => sub {
662 Koha::Notice::Template->new(
667 message_transport_type => 'email'
672 $letter = C4::Letters::GetPreparedLetter(
674 letter_code => 'test',
677 qr{^ERROR: nothing to substitute},
678 'GetPreparedLetter should warn if tables, substiture and repeat are not set';
680 'No letter should be returned by GetPreparedLetter if something went wrong'
684 $letter = C4::Letters::GetPreparedLetter(
686 letter_code => 'test',
690 qr{^ERROR: nothing to substitute},
691 'GetPreparedLetter should warn if tables, substiture and repeat are not set, even if the key is passed';
693 'No letter should be returned by GetPreparedLetter if something went wrong'
700 subtest 'TranslateNotices' => sub {
703 t::lib::Mocks::mock_preference( 'TranslateNotices', '1' );
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');
712 my $letter = C4::Letters::GetPreparedLetter(
715 letter_code => 'code',
716 message_transport_type => 'email',
717 substitute => $substitute,
722 'GetPreparedLetter should return the default one if the lang parameter is not provided'
725 $letter = C4::Letters::GetPreparedLetter(
728 letter_code => 'code',
729 message_transport_type => 'email',
730 substitute => $substitute,
733 is( $letter->{title}, 'una prueba',
734 'GetPreparedLetter should return the required notice if it exists' );
736 $letter = C4::Letters::GetPreparedLetter(
739 letter_code => 'code',
740 message_transport_type => 'email',
741 substitute => $substitute,
747 'GetPreparedLetter should return the default notice if the one required does not exist'
750 t::lib::Mocks::mock_preference( 'TranslateNotices', '' );
752 $letter = C4::Letters::GetPreparedLetter(
755 letter_code => 'code',
756 message_transport_type => 'email',
757 substitute => $substitute,
760 is( $letter->{title}, 'a test',
761 'GetPreparedLetter should return the default notice if pref disabled but additional language exists' );
765 subtest 'SendQueuedMessages' => sub {
769 t::lib::Mocks::mock_preference( 'SMSSendDriver', 'Email' );
770 t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', '');
772 my $patron = Koha::Patrons->find($borrowernumber);
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
778 eval { C4::Letters::SendQueuedMessages(); };
779 is( $@, '', 'SendQueuedMessages should not explode if the patron does not have a sms provider set' );
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
785 warning_like { C4::Letters::SendQueuedMessages(); }
786 qr|Fake send_or_die|,
787 "SendAlerts is using the mocked send_or_die routine (claimissues)";
789 my $message = $schema->resultset('MessageQueue')->search({
790 borrowernumber => $borrowernumber,
794 is( $message->to_address(), '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address not set' );
796 $message->from_address(),
798 'SendQueuedMessages uses message queue item \"from address\" for SMS by email when EmailSMSSendDriverFromAddress system preference is not set'
801 $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber, status => 'sent'})->delete(); #clear borrower queue
803 t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', 'override@example.com');
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)";
810 $message = $schema->resultset('MessageQueue')->search({
811 borrowernumber => $borrowernumber,
816 $message->from_address(),
817 'override@example.com',
818 'SendQueuedMessages uses EmailSMSSendDriverFromAddress value for SMS by email when EmailSMSSendDriverFromAddress is set'
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);
825 my $number_attempted = C4::Letters::SendQueuedMessages({
826 borrowernumber => -1, # -1 still triggers the borrowernumber condition
827 letter_code => 'PASSWORD_RESET',
829 is ( $number_attempted, 0, 'There were no password reset messages for SendQueuedMessages to attempt.' );
831 warning_like { C4::Letters::SendQueuedMessages(); }
832 qr|Fake send_or_die|,
833 "SendAlerts is using the mocked send_or_die routine (claimissues)";
835 my $sms_message_address = $schema->resultset('MessageQueue')->search({
836 borrowernumber => $borrowernumber,
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' );
843 subtest 'get_item_content' => sub {
846 t::lib::Mocks::mock_preference('dateformat', 'metric');
847 t::lib::Mocks::mock_preference('timeformat', '24hr');
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 },
852 my @item_content_fields = qw( date_due title barcode author itemnumber );
855 for my $item ( @items ) {
856 $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields } );
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
863 is( $items_content, $expected_items_content, 'get_item_content should return correct items info with time (default)' );
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, } );
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
875 is( $items_content, $expected_items_content, 'get_item_content should return correct items info without time (if dateonly => 1)' );
878 subtest 'Test limit parameter for SendQueuedMessages' => sub {
881 my $dbh = C4::Context->dbh;
883 my $borrowernumber = Koha::Patron->new({
886 categorycode => $patron_category,
887 branchcode => $library->{branchcode},
888 dateofbirth => $date,
889 smsalertnumber => undef,
890 })->store->borrowernumber;
892 $dbh->do(q|DELETE FROM message_queue|);
895 'content' => 'a message',
896 'metadata' => 'metadata',
897 'code' => 'TEST_MESSAGE',
898 'content_type' => 'text/plain',
899 'title' => 'message title'
901 'borrowernumber' => $borrowernumber,
902 'to_address' => undef,
903 'message_transport_type' => 'sms',
904 'from_address' => 'from@example.com'
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' );
922 subtest 'Test message_id parameter for SendQueuedMessages' => sub {
926 my $dbh = C4::Context->dbh;
928 my $borrowernumber = Koha::Patron->new({
931 categorycode => $patron_category,
932 branchcode => $library->{branchcode},
933 dateofbirth => $date,
934 smsalertnumber => undef,
935 })->store->borrowernumber;
937 $dbh->do(q|DELETE FROM message_queue|);
940 'content' => 'a message',
941 'metadata' => 'metadata',
942 'code' => 'TEST_MESSAGE',
943 'content_type' => 'text/plain',
944 'title' => 'message title'
946 'borrowernumber' => $borrowernumber,
947 'to_address' => 'to@example.org',
948 'message_transport_type' => 'email',
949 'from_address' => 'root@localhost.' # invalid KohaAdminEmailAddress
951 my $message_id = C4::Letters::EnqueueLetter($my_message);
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' );
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' );