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 => 77;
29 my ( $email_object, $sendmail_params );
31 my $email_sender_module = Test::MockModule->new('Email::Stuffer');
32 $email_sender_module->mock(
35 ( $email_object, $sendmail_params ) = @_;
36 my $str = $email_object->email->as_string;
37 unlike $str, qr/I =C3=A2=C2=99=C2=A5 Koha=/, "Content is not double encoded";
38 warn "Fake send_or_die";
42 use_ok('C4::Context');
43 use_ok('C4::Members');
44 use_ok('C4::Acquisition');
46 use_ok('C4::Letters');
48 use t::lib::TestBuilder;
50 use Koha::DateUtils qw( dt_from_string output_pref );
51 use Koha::Acquisition::Booksellers;
52 use Koha::Acquisition::Bookseller::Contacts;
53 use Koha::Acquisition::Orders;
55 use Koha::Notice::Templates;
57 use Koha::Subscriptions;
58 my $schema = Koha::Database->schema;
59 $schema->storage->txn_begin();
61 my $builder = t::lib::TestBuilder->new;
62 my $dbh = C4::Context->dbh;
64 $dbh->do(q|DELETE FROM letter|);
65 $dbh->do(q|DELETE FROM message_queue|);
66 $dbh->do(q|DELETE FROM message_transport_types|);
68 my $library = $builder->build({
71 branchemail => 'branchemail@address.com',
72 branchreplyto => 'branchreplyto@address.com',
73 branchreturnpath => 'branchreturnpath@address.com',
76 my $patron_category = $builder->build({ source => 'Category' })->{categorycode};
77 my $date = dt_from_string;
78 my $borrowernumber = Koha::Patron->new({
81 categorycode => $patron_category,
82 branchcode => $library->{branchcode},
84 smsalertnumber => undef,
85 })->store->borrowernumber;
87 my $marc_record = MARC::Record->new;
88 my( $biblionumber, $biblioitemnumber ) = AddBiblio( $marc_record, '' );
92 # GetMessageTransportTypes
93 my $mtts = C4::Letters::GetMessageTransportTypes();
94 is( @$mtts, 0, 'GetMessageTransportTypes returns the correct number of message types' );
97 INSERT INTO message_transport_types( message_transport_type ) VALUES ('email'), ('phone'), ('print'), ('sms')
99 $mtts = C4::Letters::GetMessageTransportTypes();
100 is_deeply( $mtts, ['email', 'phone', 'print', 'sms'], 'GetMessageTransportTypes returns all values' );
104 is( C4::Letters::EnqueueLetter(), undef, 'EnqueueLetter without argument returns undef' );
107 borrowernumber => $borrowernumber,
108 message_transport_type => 'sms',
110 from_address => 'from@example.com',
112 my $message_id = C4::Letters::EnqueueLetter($my_message);
113 is( $message_id, undef, 'EnqueueLetter without the letter argument returns undef' );
115 delete $my_message->{message_transport_type};
116 $my_message->{letter} = {
117 content => 'I ♥ Koha',
118 title => '啤酒 is great',
119 metadata => 'metadata',
120 code => 'TEST_MESSAGE',
121 content_type => 'text/plain',
124 $message_id = C4::Letters::EnqueueLetter($my_message);
125 is( $message_id, undef, 'EnqueueLetter without the message type argument argument returns undef' );
127 $my_message->{message_transport_type} = 'sms';
128 $message_id = C4::Letters::EnqueueLetter($my_message);
129 ok(defined $message_id && $message_id > 0, 'new message successfully queued');
133 my $messages = C4::Letters::GetQueuedMessages();
134 is( @$messages, 1, 'GetQueuedMessages without argument returns all the entries' );
136 $messages = C4::Letters::GetQueuedMessages({ borrowernumber => $borrowernumber });
137 is( @$messages, 1, 'one message stored for the borrower' );
138 is( $messages->[0]->{message_id}, $message_id, 'EnqueueLetter returns the message id correctly' );
139 is( $messages->[0]->{borrowernumber}, $borrowernumber, 'EnqueueLetter stores the borrower number correctly' );
140 is( $messages->[0]->{subject}, $my_message->{letter}->{title}, 'EnqueueLetter stores the subject correctly' );
141 is( $messages->[0]->{content}, $my_message->{letter}->{content}, 'EnqueueLetter stores the content correctly' );
142 is( $messages->[0]->{message_transport_type}, $my_message->{message_transport_type}, 'EnqueueLetter stores the message type correctly' );
143 is( $messages->[0]->{status}, 'pending', 'EnqueueLetter stores the status pending correctly' );
144 isnt( $messages->[0]->{time_queued}, undef, 'Time queued inserted by default in message_queue table' );
145 is( $messages->[0]->{updated_on}, $messages->[0]->{time_queued}, 'Time status changed equals time queued when created in message_queue table' );
147 # Setting time_queued to something else than now
148 my $yesterday = dt_from_string->subtract( days => 1 );
149 Koha::Notice::Messages->find($messages->[0]->{message_id})->time_queued($yesterday)->store;
152 my $messages_processed = C4::Letters::SendQueuedMessages( { type => 'email' });
153 is($messages_processed, 0, 'No queued messages processed if type limit passed with unused type');
154 $messages_processed = C4::Letters::SendQueuedMessages( { type => 'sms' });
155 is($messages_processed, 1, 'All queued messages processed, found correct number of messages with type limit');
156 $messages = C4::Letters::GetQueuedMessages({ borrowernumber => $borrowernumber });
158 $messages->[0]->{status},
160 'message marked failed if tried to send SMS message for borrower with no smsalertnumber set (bug 11208)'
162 isnt($messages->[0]->{updated_on}, $messages->[0]->{time_queued}, 'Time status changed differs from time queued when status changes' );
163 is(dt_from_string($messages->[0]->{time_queued}), $yesterday, 'Time queued remaines inmutable' );
166 my $resent = C4::Letters::ResendMessage($messages->[0]->{message_id});
167 my $message = C4::Letters::GetMessage( $messages->[0]->{message_id});
168 is( $resent, 1, 'The message should have been resent' );
169 is($message->{status},'pending', 'ResendMessage sets status to pending correctly (bug 12426)');
170 $resent = C4::Letters::ResendMessage($messages->[0]->{message_id});
171 is( $resent, 0, 'The message should not have been resent again' );
172 $resent = C4::Letters::ResendMessage();
173 is( $resent, undef, 'ResendMessage should return undef if not message_id given' );
176 my $letters = C4::Letters::GetLetters();
177 is( @$letters, 0, 'GetLetters returns the correct number of letters' );
179 my $title = q|<<branches.branchname>> - <<status>>|;
180 my $content = q{Dear <<borrowers.firstname>> <<borrowers.surname>>,
181 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.
183 <<branches.branchname>>
184 <<branches.branchaddress1>>
187 The following item(s) is/are currently <<status>>:
189 <item> <<count>>. <<items.itemcallnumber>>, Barcode: <<items.barcode>> </item>
191 Thank-you for your prompt attention to this matter.
192 Don't forget your date of birth: <<borrowers.dateofbirth>>.
193 Look at this wonderful biblio timestamp: <<biblio.timestamp>>.
196 $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 );
197 $letters = C4::Letters::GetLetters();
198 is( @$letters, 1, 'GetLetters returns the correct number of letters' );
199 is( $letters->[0]->{module}, 'my module', 'GetLetters gets the module correctly' );
200 is( $letters->[0]->{code}, 'my code', 'GetLetters gets the code correctly' );
201 is( $letters->[0]->{name}, 'my name', 'GetLetters gets the name correctly' );
205 subtest 'getletter' => sub {
207 t::lib::Mocks::mock_preference('IndependentBranches', 0);
208 my $letter = C4::Letters::getletter('my module', 'my code', $library->{branchcode}, 'email');
209 is( $letter->{branchcode}, $library->{branchcode}, 'GetLetters gets the branch code correctly' );
210 is( $letter->{module}, 'my module', 'GetLetters gets the module correctly' );
211 is( $letter->{code}, 'my code', 'GetLetters gets the code correctly' );
212 is( $letter->{name}, 'my name', 'GetLetters gets the name correctly' );
213 is( $letter->{is_html}, 1, 'GetLetters gets the boolean is_html correctly' );
214 is( $letter->{title}, $title, 'GetLetters gets the title correctly' );
215 is( $letter->{content}, $content, 'GetLetters gets the content correctly' );
216 is( $letter->{message_transport_type}, 'email', 'GetLetters gets the message type correctly' );
218 t::lib::Mocks::mock_userenv({ branchcode => "anotherlib", flags => 1 });
220 t::lib::Mocks::mock_preference('IndependentBranches', 1);
221 $letter = C4::Letters::getletter('my module', 'my code', $library->{branchcode}, 'email');
222 is( $letter->{branchcode}, $library->{branchcode}, 'GetLetters gets the branch code correctly' );
223 is( $letter->{module}, 'my module', 'GetLetters gets the module correctly' );
224 is( $letter->{code}, 'my code', 'GetLetters gets the code correctly' );
225 is( $letter->{name}, 'my name', 'GetLetters gets the name correctly' );
226 is( $letter->{is_html}, 1, 'GetLetters gets the boolean is_html correctly' );
227 is( $letter->{title}, $title, 'GetLetters gets the title correctly' );
228 is( $letter->{content}, $content, 'GetLetters gets the content correctly' );
229 is( $letter->{message_transport_type}, 'email', 'GetLetters gets the message type correctly' );
234 # Regression test for Bug 14206
235 $dbh->do( q|INSERT INTO letter(branchcode,module,code,name,is_html,title,content,message_transport_type) VALUES ('FFL','my module','my code','my name',1,?,?,'print')|, undef, $title, $content );
236 my $letter14206_a = C4::Letters::getletter('my module', 'my code', 'FFL' );
237 is( $letter14206_a->{message_transport_type}, 'print', 'Bug 14206 - message_transport_type not passed, correct mtt detected' );
238 my $letter14206_b = C4::Letters::getletter('my module', 'my code', 'FFL', 'print');
239 is( $letter14206_b->{message_transport_type}, 'print', 'Bug 14206 - message_transport_type passed, correct mtt detected' );
241 # test for overdue_notices.pl
242 my $overdue_rules = {
243 letter1 => 'my code',
246 my $branchcode = 'FFL';
247 my $letter14206_c = C4::Letters::getletter('my module', $overdue_rules->{"letter$i"}, $branchcode);
248 is( $letter14206_c->{message_transport_type}, 'print', 'Bug 14206 - correct mtt detected for call from overdue_notices.pl' );
251 t::lib::Mocks::mock_preference('OPACBaseURL', 'http://thisisatest.com');
252 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', '' );
254 my $sms_content = 'This is a SMS for an <<status>>';
255 $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 );
258 borrowers => $borrowernumber,
259 branches => $library->{branchcode},
260 biblio => $biblionumber,
267 itemcallnumber => 'my callnumber1',
271 itemcallnumber => 'my callnumber2',
275 my $prepared_letter = GetPreparedLetter((
276 module => 'my module',
277 branchcode => $library->{branchcode},
278 letter_code => 'my code',
280 substitute => $substitute,
283 my $retrieved_library = Koha::Libraries->find($library->{branchcode});
284 my $my_title_letter = $retrieved_library->branchname . qq| - $substitute->{status}|;
285 my $biblio_timestamp = dt_from_string( GetBiblioData($biblionumber)->{timestamp} );
286 my $my_content_letter = qq|Dear Jane Smith,
287 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.
289 |.$retrieved_library->branchname.qq|
290 |.$retrieved_library->branchaddress1.qq|
291 URL: http://thisisatest.com
293 The following item(s) is/are currently $substitute->{status}:
295 <item> 1. $repeat->[0]->{itemcallnumber}, Barcode: $repeat->[0]->{barcode} </item>
296 <item> 2. $repeat->[1]->{itemcallnumber}, Barcode: $repeat->[1]->{barcode} </item>
298 Thank-you for your prompt attention to this matter.
299 Don't forget your date of birth: | . output_pref({ dt => $date, dateonly => 1 }) . q|.
300 Look at this wonderful biblio timestamp: | . output_pref({ dt => $biblio_timestamp }) . ".\n";
302 is( $prepared_letter->{title}, $my_title_letter, 'GetPreparedLetter returns the title correctly' );
303 is( $prepared_letter->{content}, $my_content_letter, 'GetPreparedLetter returns the content correctly' );
305 $prepared_letter = GetPreparedLetter((
306 module => 'my module',
307 branchcode => $library->{branchcode},
308 letter_code => 'my code',
310 substitute => $substitute,
312 message_transport_type => 'sms',
314 $my_content_letter = qq|This is a SMS for an $substitute->{status}|;
315 is( $prepared_letter->{content}, $my_content_letter, 'GetPreparedLetter returns the content correctly' );
317 $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>>.');});
318 $prepared_letter = GetPreparedLetter((
319 module => 'test_date',
321 letter_code => 'test_date',
323 substitute => $substitute,
326 is( $prepared_letter->{content}, q|This one only contains the date: | . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 1' );
328 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp | dateonly>>.' WHERE code = 'test_date';});
329 $prepared_letter = GetPreparedLetter((
330 module => 'test_date',
332 letter_code => 'test_date',
334 substitute => $substitute,
337 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 2' );
339 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp|dateonly >>.' WHERE code = 'test_date';});
340 $prepared_letter = GetPreparedLetter((
341 module => 'test_date',
343 letter_code => 'test_date',
345 substitute => $substitute,
348 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 3' );
350 t::lib::Mocks::mock_preference( 'TimeFormat', '12hr' );
351 my $yesterday_night = $date->clone->add( days => -1 )->set_hour(22);
352 $dbh->do(q|UPDATE biblio SET timestamp = ? WHERE biblionumber = ?|, undef, $yesterday_night, $biblionumber );
353 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp>>.' WHERE code = 'test_date';});
354 $prepared_letter = GetPreparedLetter((
355 module => 'test_date',
357 letter_code => 'test_date',
359 substitute => $substitute,
362 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $yesterday_night }) . q|.|, 'dateonly test 3' );
364 $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>');});
365 $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>');});
367 # Test that _parseletter doesn't modify its parameters bug 15429
369 my $values = { dateexpiry => '2015-12-13', };
370 C4::Letters::_parseletter($prepared_letter, 'borrowers', $values);
371 is( $values->{dateexpiry}, '2015-12-13', "_parseletter doesn't modify its parameters" );
374 # Correctly format dateexpiry
376 my $values = { dateexpiry => '2015-12-13', };
378 t::lib::Mocks::mock_preference('dateformat', 'metric');
379 t::lib::Mocks::mock_preference('timeformat', '24hr');
380 my $letter = C4::Letters::_parseletter({ content => "expiry on <<borrowers.dateexpiry>>"}, 'borrowers', $values);
381 is( $letter->{content}, 'expiry on 13/12/2015' );
383 t::lib::Mocks::mock_preference('dateformat', 'metric');
384 t::lib::Mocks::mock_preference('timeformat', '12hr');
385 $letter = C4::Letters::_parseletter({ content => "expiry on <<borrowers.dateexpiry>>"}, 'borrowers', $values);
386 is( $letter->{content}, 'expiry on 13/12/2015' );
389 my $bookseller = Koha::Acquisition::Bookseller->new(
392 address1 => "bookseller's address",
398 my $booksellerid = $bookseller->id;
400 Koha::Acquisition::Bookseller::Contact->new( { name => 'John Smith', phone => '0123456x1', claimacquisition => 1, orderacquisition => 1, booksellerid => $booksellerid } )->store;
401 Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues => 1, booksellerid => $booksellerid } )->store;
402 my $basketno = NewBasket($booksellerid, 1);
404 my $budgetid = C4::Budgets::AddBudget({
405 budget_code => "budget_code_test_letters",
406 budget_name => "budget_name_test_letters",
409 my $bib = MARC::Record->new();
410 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
412 MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
416 MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
420 my $logged_in_user = $builder->build_object(
422 class => 'Koha::Patrons',
424 branchcode => $library->{branchcode},
425 email => 'some@email.com'
430 t::lib::Mocks::mock_userenv({ patron => $logged_in_user });
432 ($biblionumber, $biblioitemnumber) = AddBiblio($bib, '');
433 my $order = Koha::Acquisition::Order->new(
435 basketno => $basketno,
437 biblionumber => $biblionumber,
438 budget_id => $budgetid,
441 my $ordernumber = $order->ordernumber;
443 Koha::Acquisition::Baskets->find( $basketno )->close;
446 $err = SendAlerts( 'claimacquisition', [ $ordernumber ], 'TESTACQCLAIM' ) }
447 qr/^Bookseller .* without emails at/,
448 "SendAlerts prints a warning";
449 is($err->{'error'}, 'no_email', "Trying to send an alert when there's no e-mail results in an error");
451 $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
452 $bookseller->contacts->next->email('testemail@mydomain.com')->store;
454 # Ensure that the preference 'LetterLog' is set to logging
455 t::lib::Mocks::mock_preference( 'LetterLog', 'on' );
457 # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
458 t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
462 $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ) }
463 qr|Fake send_or_die|,
464 "SendAlerts is using the mocked sendmail routine (orderacquisition)";
465 is($err, 1, "Successfully sent order.");
466 is($email_object->email->header('To'), 'testemail@mydomain.com', "mailto correct in sent order");
467 is($email_object->email->body, 'my vendor|John Smith|Ordernumber ' . $ordernumber . ' (Silence in the library) (1 ordered)', 'Order notice text constructed successfully');
469 $dbh->do(q{DELETE FROM letter WHERE code = 'TESTACQORDER';});
471 $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ) }
472 qr/No orderacquisition TESTACQORDER letter transported by email/,
473 "GetPreparedLetter warns about missing notice template";
474 is($err->{'error'}, 'no_letter', "No TESTACQORDER letter was defined.");
479 $err = SendAlerts( 'claimacquisition', [ $ordernumber ], 'TESTACQCLAIM' ) }
480 qr|Fake send_or_die|,
481 "SendAlerts is using the mocked sendmail routine";
483 is($err, 1, "Successfully sent claim");
484 is($email_object->email->header('To'), 'testemail@mydomain.com', "mailto correct in sent claim");
485 is($email_object->email->body, 'my vendor|John Smith|Ordernumber ' . $ordernumber . ' (Silence in the library) (1 ordered)', 'Claim notice text constructed successfully');
492 my $internalnotes = 'intnotes';
493 $dbh->do(q|UPDATE subscription_numberpatterns SET numberingmethod='No. {X}' WHERE id=1|);
494 my $subscriptionid = NewSubscription(
495 undef, "", undef, undef, undef, $biblionumber,
496 '2013-01-01', 1, undef, undef, undef,
497 undef, undef, undef, undef, undef, undef,
498 1, $notes,undef, '2013-01-01', undef, 1,
499 undef, undef, 0, $internalnotes, 0,
500 undef, undef, 0, undef, '2013-12-31', 0
502 $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>>');});
503 my ($serials_count, @serials) = GetSerials($subscriptionid);
504 my $serial = $serials[0];
506 my $patron = Koha::Patron->new({
509 categorycode => $patron_category,
510 branchcode => $library->{branchcode},
511 dateofbirth => $date,
512 email => 'john.smith@test.de',
514 my $borrowernumber = $patron->borrowernumber;
515 my $subscription = Koha::Subscriptions->find( $subscriptionid );
516 $subscription->add_subscriber( $patron );
518 t::lib::Mocks::mock_userenv({ branch => $library->{branchcode} });
521 $err2 = SendAlerts( 'issue', $serial->{serialid}, 'RLIST' ) }
522 qr|Fake send_or_die|,
523 "SendAlerts is using the mocked sendmail routine";
525 is($err2, 1, "Successfully sent serial notification");
526 is($email_object->email->header('To'), 'john.smith@test.de', "mailto correct in sent serial notification");
527 is($email_object->email->body, 'Silence in the library,'.$subscriptionid.',No. 0', 'Serial notification text constructed successfully');
529 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', 'robert.tables@mail.com' );
533 $err3 = SendAlerts( 'issue', $serial->{serialid}, 'RLIST' ) }
534 qr|Fake send_or_die|,
535 "SendAlerts is using the mocked sendmail routine";
536 is($email_object->email->header('To'), 'robert.tables@mail.com', "mailto address overwritten by SendAllMailsTo preference");
538 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', '' );
540 subtest 'SendAlerts - claimissue' => sub {
545 $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>>');});
547 my $bookseller = Koha::Acquisition::Bookseller->new(
550 address1 => "bookseller's address",
556 my $booksellerid = $bookseller->id;
558 Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues => 1, booksellerid => $booksellerid } )->store;
560 my $bib = MARC::Record->new();
561 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
563 MARC::Field->new('011', ' ', ' ', a => 'xxxx-yyyy'),
564 MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
568 MARC::Field->new('022', ' ', ' ', a => 'xxxx-yyyy'),
569 MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
572 my ($biblionumber) = AddBiblio($bib, '');
574 $dbh->do(q|UPDATE subscription_numberpatterns SET numberingmethod='No. {X}' WHERE id=1|);
575 my $subscriptionid = NewSubscription(
576 undef, "", $booksellerid, undef, undef, $biblionumber,
577 '2013-01-01', 1, undef, undef, undef,
578 undef, undef, undef, undef, undef, undef,
579 1, 'public',undef, '2013-01-01', undef, 1,
580 undef, undef, 0, 'internal', 0,
581 undef, undef, 0, undef, '2013-12-31', 0
584 my ($serials_count, @serials) = GetSerials($subscriptionid);
585 my @serialids = ($serials[0]->{serialid});
589 $err = SendAlerts( 'claimissues', \@serialids, 'TESTSERIALCLAIM' ) }
590 qr/^Bookseller .* without emails at/,
591 "Warn on vendor without email address";
593 $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
594 $bookseller->contacts->next->email('testemail@mydomain.com')->store;
596 # Ensure that the preference 'LetterLog' is set to logging
597 t::lib::Mocks::mock_preference( 'LetterLog', 'on' );
599 # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
600 t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
602 t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
606 $err = SendAlerts( 'claimissues', \@serialids , 'TESTSERIALCLAIM' ) }
607 qr|Fake send_or_die|,
608 "SendAlerts is using the mocked sendmail routine (claimissues)";
609 is( $err, 1, "Successfully sent claim" );
610 is( $email_object->email->header('To'),
611 'testemail@mydomain.com', "mailto correct in sent claim" );
613 $email_object->email->body,
614 "$serialids[0]|2013-01-01|Silence in the library|xxxx-yyyy",
615 'Serial claim letter for 1 issue constructed successfully'
620 my $publisheddate = output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 });
621 my $serialexpected = ( C4::Serials::findSerialsByStatus( 1, $subscriptionid ) )[0];
622 ModSerialStatus( $serials[0]->{serialid}, "No. 1", $publisheddate, $publisheddate, $publisheddate, '3', 'a note' );
623 ($serials_count, @serials) = GetSerials($subscriptionid);
624 push @serialids, ($serials[1]->{serialid});
626 warning_like { $err = SendAlerts( 'claimissues', \@serialids, 'TESTSERIALCLAIM' ); }
627 qr|Fake send_or_die|,
628 "SendAlerts is using the mocked sendmail routine (claimissues)";
631 $email_object->email->body,
632 "$serialids[0]|2013-01-01|Silence in the library|xxxx-yyyy"
633 . $email_object->email->crlf
634 . "$serialids[1]|2013-01-01|Silence in the library|xxxx-yyyy",
635 "Serial claim letter for 2 issues constructed successfully"
638 $dbh->do(q{DELETE FROM letter WHERE code = 'TESTSERIALCLAIM';});
640 $err = SendAlerts( 'orderacquisition', $basketno , 'TESTSERIALCLAIM' ) }
641 qr/No orderacquisition TESTSERIALCLAIM letter transported by email/,
642 "GetPreparedLetter warns about missing notice template";
643 is($err->{'error'}, 'no_letter', "No TESTSERIALCLAIM letter was defined");
648 subtest 'GetPreparedLetter' => sub {
651 Koha::Notice::Template->new(
656 message_transport_type => 'email'
661 $letter = C4::Letters::GetPreparedLetter(
663 letter_code => 'test',
666 qr{^ERROR: nothing to substitute},
667 'GetPreparedLetter should warn if tables, substiture and repeat are not set';
669 'No letter should be returned by GetPreparedLetter if something went wrong'
673 $letter = C4::Letters::GetPreparedLetter(
675 letter_code => 'test',
679 qr{^ERROR: nothing to substitute},
680 'GetPreparedLetter should warn if tables, substiture and repeat are not set, even if the key is passed';
682 'No letter should be returned by GetPreparedLetter if something went wrong'
689 subtest 'TranslateNotices' => sub {
692 t::lib::Mocks::mock_preference( 'TranslateNotices', '1' );
696 INSERT INTO letter (module, code, branchcode, name, title, content, message_transport_type, lang) VALUES
697 ('test', 'code', '', 'test', 'a test', 'just a test', 'email', 'default'),
698 ('test', 'code', '', 'test', 'una prueba', 'solo una prueba', 'email', 'es-ES');
701 my $letter = C4::Letters::GetPreparedLetter(
704 letter_code => 'code',
705 message_transport_type => 'email',
706 substitute => $substitute,
711 'GetPreparedLetter should return the default one if the lang parameter is not provided'
714 $letter = C4::Letters::GetPreparedLetter(
717 letter_code => 'code',
718 message_transport_type => 'email',
719 substitute => $substitute,
722 is( $letter->{title}, 'una prueba',
723 'GetPreparedLetter should return the required notice if it exists' );
725 $letter = C4::Letters::GetPreparedLetter(
728 letter_code => 'code',
729 message_transport_type => 'email',
730 substitute => $substitute,
736 'GetPreparedLetter should return the default notice if the one required does not exist'
739 t::lib::Mocks::mock_preference( 'TranslateNotices', '' );
741 $letter = C4::Letters::GetPreparedLetter(
744 letter_code => 'code',
745 message_transport_type => 'email',
746 substitute => $substitute,
749 is( $letter->{title}, 'a test',
750 'GetPreparedLetter should return the default notice if pref disabled but additional language exists' );
754 subtest 'SendQueuedMessages' => sub {
758 t::lib::Mocks::mock_preference( 'SMSSendDriver', 'Email' );
759 t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', '');
761 my $patron = Koha::Patrons->find($borrowernumber);
763 INSERT INTO message_queue(borrowernumber, subject, content, message_transport_type, status, letter_code)
764 VALUES (?, 'subject', 'content', 'sms', 'pending', 'just_a_code')
765 |, undef, $borrowernumber
767 eval { C4::Letters::SendQueuedMessages(); };
768 is( $@, '', 'SendQueuedMessages should not explode if the patron does not have a sms provider set' );
770 my $sms_pro = $builder->build_object({ class => 'Koha::SMS::Providers', value => { domain => 'kidclamp.rocks' } });
771 $patron->set( { smsalertnumber => '5555555555', sms_provider_id => $sms_pro->id() } )->store;
772 $message_id = C4::Letters::EnqueueLetter($my_message); #using datas set around line 95 and forward
774 warning_like { C4::Letters::SendQueuedMessages(); }
775 qr|Fake send_or_die|,
776 "SendAlerts is using the mocked sendmail routine (claimissues)";
778 my $message = $schema->resultset('MessageQueue')->search({
779 borrowernumber => $borrowernumber,
783 is( $message->to_address(), '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address not set' );
785 $message->from_address(),
787 'SendQueuedMessages uses message queue item \"from address\" for SMS by email when EmailSMSSendDriverFromAddress system preference is not set'
790 $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber, status => 'sent'})->delete(); #clear borrower queue
792 t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', 'override@example.com');
794 $message_id = C4::Letters::EnqueueLetter($my_message);
795 warning_like { C4::Letters::SendQueuedMessages(); }
796 qr|Fake send_or_die|,
797 "SendAlerts is using the mocked sendmail routine (claimissues)";
799 $message = $schema->resultset('MessageQueue')->search({
800 borrowernumber => $borrowernumber,
805 $message->from_address(),
806 'override@example.com',
807 'SendQueuedMessages uses EmailSMSSendDriverFromAddress value for SMS by email when EmailSMSSendDriverFromAddress is set'
810 $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber,status => 'sent'})->delete(); #clear borrower queue
811 $my_message->{to_address} = 'fixme@kidclamp.iswrong';
812 $message_id = C4::Letters::EnqueueLetter($my_message);
814 my $number_attempted = C4::Letters::SendQueuedMessages({
815 borrowernumber => -1, # -1 still triggers the borrowernumber condition
816 letter_code => 'PASSWORD_RESET',
818 is ( $number_attempted, 0, 'There were no password reset messages for SendQueuedMessages to attempt.' );
820 warning_like { C4::Letters::SendQueuedMessages(); }
821 qr|Fake send_or_die|,
822 "SendAlerts is using the mocked sendmail routine (claimissues)";
824 my $sms_message_address = $schema->resultset('MessageQueue')->search({
825 borrowernumber => $borrowernumber,
827 })->next()->to_address();
828 is( $sms_message_address, '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address is set incorrectly' );
832 subtest 'get_item_content' => sub {
835 t::lib::Mocks::mock_preference('dateformat', 'metric');
836 t::lib::Mocks::mock_preference('timeformat', '24hr');
838 {date_due => '2041-01-01 12:34', title => 'a first title', barcode => 'a_first_barcode', author => 'a_first_author', itemnumber => 1 },
839 {date_due => '2042-01-02 23:45', title => 'a second title', barcode => 'a_second_barcode', author => 'a_second_author', itemnumber => 2 },
841 my @item_content_fields = qw( date_due title barcode author itemnumber );
844 for my $item ( @items ) {
845 $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields } );
848 my $expected_items_content = <<EOF;
849 01/01/2041 12:34\ta first title\ta_first_barcode\ta_first_author\t1
850 02/01/2042 23:45\ta second title\ta_second_barcode\ta_second_author\t2
852 is( $items_content, $expected_items_content, 'get_item_content should return correct items info with time (default)' );
855 $items_content = q||;
856 for my $item ( @items ) {
857 $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields, dateonly => 1, } );
860 $expected_items_content = <<EOF;
861 01/01/2041\ta first title\ta_first_barcode\ta_first_author\t1
862 02/01/2042\ta second title\ta_second_barcode\ta_second_author\t2
864 is( $items_content, $expected_items_content, 'get_item_content should return correct items info without time (if dateonly => 1)' );
867 subtest 'Test limit parameter for SendQueuedMessages' => sub {
870 my $dbh = C4::Context->dbh;
872 my $borrowernumber = Koha::Patron->new({
875 categorycode => $patron_category,
876 branchcode => $library->{branchcode},
877 dateofbirth => $date,
878 smsalertnumber => undef,
879 })->store->borrowernumber;
881 $dbh->do(q|DELETE FROM message_queue|);
884 'content' => 'a message',
885 'metadata' => 'metadata',
886 'code' => 'TEST_MESSAGE',
887 'content_type' => 'text/plain',
888 'title' => 'message title'
890 'borrowernumber' => $borrowernumber,
891 'to_address' => undef,
892 'message_transport_type' => 'sms',
893 'from_address' => 'from@example.com'
895 C4::Letters::EnqueueLetter($my_message);
896 C4::Letters::EnqueueLetter($my_message);
897 C4::Letters::EnqueueLetter($my_message);
898 C4::Letters::EnqueueLetter($my_message);
899 C4::Letters::EnqueueLetter($my_message);
900 my $messages_processed = C4::Letters::SendQueuedMessages( { limit => 1 } );
901 is( $messages_processed, 1,
902 'Processed 1 message with limit of 1 and 5 unprocessed messages' );
903 $messages_processed = C4::Letters::SendQueuedMessages( { limit => 2 } );
904 is( $messages_processed, 2,
905 'Processed 2 message with limit of 2 and 4 unprocessed messages' );
906 $messages_processed = C4::Letters::SendQueuedMessages( { limit => 3 } );
907 is( $messages_processed, 2,
908 'Processed 2 message with limit of 3 and 2 unprocessed messages' );