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 => 67;
28 my $module = new Test::MockModule('Mail::Sendmail');
37 use_ok('C4::Context');
38 use_ok('C4::Members');
39 use_ok('C4::Acquisition');
41 use_ok('C4::Letters');
43 use t::lib::TestBuilder;
45 use Koha::DateUtils qw( dt_from_string output_pref );
46 use Koha::Acquisition::Booksellers;
47 use Koha::Acquisition::Bookseller::Contacts;
48 use Koha::Acquisition::Orders;
50 use Koha::Notice::Templates;
52 use Koha::Subscriptions;
53 my $schema = Koha::Database->schema;
54 $schema->storage->txn_begin();
56 my $builder = t::lib::TestBuilder->new;
57 my $dbh = C4::Context->dbh;
58 $dbh->{RaiseError} = 1;
60 $dbh->do(q|DELETE FROM letter|);
61 $dbh->do(q|DELETE FROM message_queue|);
62 $dbh->do(q|DELETE FROM message_transport_types|);
64 my $library = $builder->build({
67 my $patron_category = $builder->build({ source => 'Category' })->{categorycode};
68 my $date = dt_from_string;
69 my $borrowernumber = Koha::Patron->new({
72 categorycode => $patron_category,
73 branchcode => $library->{branchcode},
75 smsalertnumber => undef,
76 })->store->borrowernumber;
78 my $marc_record = MARC::Record->new;
79 my( $biblionumber, $biblioitemnumber ) = AddBiblio( $marc_record, '' );
83 # GetMessageTransportTypes
84 my $mtts = C4::Letters::GetMessageTransportTypes();
85 is( @$mtts, 0, 'GetMessageTransportTypes returns the correct number of message types' );
88 INSERT INTO message_transport_types( message_transport_type ) VALUES ('email'), ('phone'), ('print'), ('sms')
90 $mtts = C4::Letters::GetMessageTransportTypes();
91 is_deeply( $mtts, ['email', 'phone', 'print', 'sms'], 'GetMessageTransportTypes returns all values' );
95 is( C4::Letters::EnqueueLetter(), undef, 'EnqueueLetter without argument returns undef' );
98 borrowernumber => $borrowernumber,
99 message_transport_type => 'sms',
101 from_address => 'from@example.com',
103 my $message_id = C4::Letters::EnqueueLetter($my_message);
104 is( $message_id, undef, 'EnqueueLetter without the letter argument returns undef' );
106 delete $my_message->{message_transport_type};
107 $my_message->{letter} = {
108 content => 'a message',
109 title => 'message title',
110 metadata => 'metadata',
111 code => 'TEST_MESSAGE',
112 content_type => 'text/plain',
115 $message_id = C4::Letters::EnqueueLetter($my_message);
116 is( $message_id, undef, 'EnqueueLetter without the message type argument argument returns undef' );
118 $my_message->{message_transport_type} = 'sms';
119 $message_id = C4::Letters::EnqueueLetter($my_message);
120 ok(defined $message_id && $message_id > 0, 'new message successfully queued');
124 my $messages = C4::Letters::GetQueuedMessages();
125 is( @$messages, 1, 'GetQueuedMessages without argument returns all the entries' );
127 $messages = C4::Letters::GetQueuedMessages({ borrowernumber => $borrowernumber });
128 is( @$messages, 1, 'one message stored for the borrower' );
129 is( $messages->[0]->{message_id}, $message_id, 'EnqueueLetter returns the message id correctly' );
130 is( $messages->[0]->{borrowernumber}, $borrowernumber, 'EnqueueLetter stores the borrower number correctly' );
131 is( $messages->[0]->{subject}, $my_message->{letter}->{title}, 'EnqueueLetter stores the subject correctly' );
132 is( $messages->[0]->{content}, $my_message->{letter}->{content}, 'EnqueueLetter stores the content correctly' );
133 is( $messages->[0]->{message_transport_type}, $my_message->{message_transport_type}, 'EnqueueLetter stores the message type correctly' );
134 is( $messages->[0]->{status}, 'pending', 'EnqueueLetter stores the status pending correctly' );
138 my $messages_processed = C4::Letters::SendQueuedMessages( { type => 'email' });
139 is($messages_processed, 0, 'No queued messages processed if type limit passed with unused type');
140 $messages_processed = C4::Letters::SendQueuedMessages( { type => 'sms' });
141 is($messages_processed, 1, 'All queued messages processed, found correct number of messages with type limit');
142 $messages = C4::Letters::GetQueuedMessages({ borrowernumber => $borrowernumber });
144 $messages->[0]->{status},
146 'message marked failed if tried to send SMS message for borrower with no smsalertnumber set (bug 11208)'
150 my $resent = C4::Letters::ResendMessage($messages->[0]->{message_id});
151 my $message = C4::Letters::GetMessage( $messages->[0]->{message_id});
152 is( $resent, 1, 'The message should have been resent' );
153 is($message->{status},'pending', 'ResendMessage sets status to pending correctly (bug 12426)');
154 $resent = C4::Letters::ResendMessage($messages->[0]->{message_id});
155 is( $resent, 0, 'The message should not have been resent again' );
156 $resent = C4::Letters::ResendMessage();
157 is( $resent, undef, 'ResendMessage should return undef if not message_id given' );
160 my $letters = C4::Letters::GetLetters();
161 is( @$letters, 0, 'GetLetters returns the correct number of letters' );
163 my $title = q|<<branches.branchname>> - <<status>>|;
164 my $content = q{Dear <<borrowers.firstname>> <<borrowers.surname>>,
165 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.
167 <<branches.branchname>>
168 <<branches.branchaddress1>>
171 The following item(s) is/are currently <<status>>:
173 <item> <<count>>. <<items.itemcallnumber>>, Barcode: <<items.barcode>> </item>
175 Thank-you for your prompt attention to this matter.
176 Don't forget your date of birth: <<borrowers.dateofbirth>>.
177 Look at this wonderful biblio timestamp: <<biblio.timestamp>>.
180 $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 );
181 $letters = C4::Letters::GetLetters();
182 is( @$letters, 1, 'GetLetters returns the correct number of letters' );
183 is( $letters->[0]->{module}, 'my module', 'GetLetters gets the module correctly' );
184 is( $letters->[0]->{code}, 'my code', 'GetLetters gets the code correctly' );
185 is( $letters->[0]->{name}, 'my name', 'GetLetters gets the name correctly' );
189 subtest 'getletter' => sub {
191 t::lib::Mocks::mock_preference('IndependentBranches', 0);
192 my $letter = C4::Letters::getletter('my module', 'my code', $library->{branchcode}, 'email');
193 is( $letter->{branchcode}, $library->{branchcode}, 'GetLetters gets the branch code correctly' );
194 is( $letter->{module}, 'my module', 'GetLetters gets the module correctly' );
195 is( $letter->{code}, 'my code', 'GetLetters gets the code correctly' );
196 is( $letter->{name}, 'my name', 'GetLetters gets the name correctly' );
197 is( $letter->{is_html}, 1, 'GetLetters gets the boolean is_html correctly' );
198 is( $letter->{title}, $title, 'GetLetters gets the title correctly' );
199 is( $letter->{content}, $content, 'GetLetters gets the content correctly' );
200 is( $letter->{message_transport_type}, 'email', 'GetLetters gets the message type correctly' );
202 t::lib::Mocks::mock_userenv({ branchcode => "anotherlib", flags => 1 });
204 t::lib::Mocks::mock_preference('IndependentBranches', 1);
205 $letter = C4::Letters::getletter('my module', 'my code', $library->{branchcode}, 'email');
206 is( $letter->{branchcode}, $library->{branchcode}, 'GetLetters gets the branch code correctly' );
207 is( $letter->{module}, 'my module', 'GetLetters gets the module correctly' );
208 is( $letter->{code}, 'my code', 'GetLetters gets the code correctly' );
209 is( $letter->{name}, 'my name', 'GetLetters gets the name correctly' );
210 is( $letter->{is_html}, 1, 'GetLetters gets the boolean is_html correctly' );
211 is( $letter->{title}, $title, 'GetLetters gets the title correctly' );
212 is( $letter->{content}, $content, 'GetLetters gets the content correctly' );
213 is( $letter->{message_transport_type}, 'email', 'GetLetters gets the message type correctly' );
218 # Regression test for Bug 14206
219 $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 );
220 my $letter14206_a = C4::Letters::getletter('my module', 'my code', 'FFL' );
221 is( $letter14206_a->{message_transport_type}, 'print', 'Bug 14206 - message_transport_type not passed, correct mtt detected' );
222 my $letter14206_b = C4::Letters::getletter('my module', 'my code', 'FFL', 'print');
223 is( $letter14206_b->{message_transport_type}, 'print', 'Bug 14206 - message_transport_type passed, correct mtt detected' );
225 # test for overdue_notices.pl
226 my $overdue_rules = {
227 letter1 => 'my code',
230 my $branchcode = 'FFL';
231 my $letter14206_c = C4::Letters::getletter('my module', $overdue_rules->{"letter$i"}, $branchcode);
232 is( $letter14206_c->{message_transport_type}, 'print', 'Bug 14206 - correct mtt detected for call from overdue_notices.pl' );
235 t::lib::Mocks::mock_preference('OPACBaseURL', 'http://thisisatest.com');
237 my $sms_content = 'This is a SMS for an <<status>>';
238 $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 );
241 borrowers => $borrowernumber,
242 branches => $library->{branchcode},
243 biblio => $biblionumber,
250 itemcallnumber => 'my callnumber1',
254 itemcallnumber => 'my callnumber2',
258 my $prepared_letter = GetPreparedLetter((
259 module => 'my module',
260 branchcode => $library->{branchcode},
261 letter_code => 'my code',
263 substitute => $substitute,
266 my $retrieved_library = Koha::Libraries->find($library->{branchcode});
267 my $my_title_letter = $retrieved_library->branchname . qq| - $substitute->{status}|;
268 my $biblio_timestamp = dt_from_string( GetBiblioData($biblionumber)->{timestamp} );
269 my $my_content_letter = qq|Dear Jane Smith,
270 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.
272 |.$retrieved_library->branchname.qq|
273 |.$retrieved_library->branchaddress1.qq|
274 URL: http://thisisatest.com
276 The following item(s) is/are currently $substitute->{status}:
278 <item> 1. $repeat->[0]->{itemcallnumber}, Barcode: $repeat->[0]->{barcode} </item>
279 <item> 2. $repeat->[1]->{itemcallnumber}, Barcode: $repeat->[1]->{barcode} </item>
281 Thank-you for your prompt attention to this matter.
282 Don't forget your date of birth: | . output_pref({ dt => $date, dateonly => 1 }) . q|.
283 Look at this wonderful biblio timestamp: | . output_pref({ dt => $biblio_timestamp }) . ".\n";
285 is( $prepared_letter->{title}, $my_title_letter, 'GetPreparedLetter returns the title correctly' );
286 is( $prepared_letter->{content}, $my_content_letter, 'GetPreparedLetter returns the content correctly' );
288 $prepared_letter = GetPreparedLetter((
289 module => 'my module',
290 branchcode => $library->{branchcode},
291 letter_code => 'my code',
293 substitute => $substitute,
295 message_transport_type => 'sms',
297 $my_content_letter = qq|This is a SMS for an $substitute->{status}|;
298 is( $prepared_letter->{content}, $my_content_letter, 'GetPreparedLetter returns the content correctly' );
300 $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>>.');});
301 $prepared_letter = GetPreparedLetter((
302 module => 'test_date',
304 letter_code => 'test_date',
306 substitute => $substitute,
309 is( $prepared_letter->{content}, q|This one only contains the date: | . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 1' );
311 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp | dateonly>>.' WHERE code = 'test_date';});
312 $prepared_letter = GetPreparedLetter((
313 module => 'test_date',
315 letter_code => 'test_date',
317 substitute => $substitute,
320 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 2' );
322 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp|dateonly >>.' WHERE code = 'test_date';});
323 $prepared_letter = GetPreparedLetter((
324 module => 'test_date',
326 letter_code => 'test_date',
328 substitute => $substitute,
331 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 3' );
333 t::lib::Mocks::mock_preference( 'TimeFormat', '12hr' );
334 my $yesterday_night = $date->clone->add( days => -1 )->set_hour(22);
335 $dbh->do(q|UPDATE biblio SET timestamp = ? WHERE biblionumber = ?|, undef, $yesterday_night, $biblionumber );
336 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp>>.' WHERE code = 'test_date';});
337 $prepared_letter = GetPreparedLetter((
338 module => 'test_date',
340 letter_code => 'test_date',
342 substitute => $substitute,
345 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $yesterday_night }) . q|.|, 'dateonly test 3' );
347 $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>');});
348 $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>');});
350 # Test that _parseletter doesn't modify its parameters bug 15429
352 my $values = { dateexpiry => '2015-12-13', };
353 C4::Letters::_parseletter($prepared_letter, 'borrowers', $values);
354 is( $values->{dateexpiry}, '2015-12-13', "_parseletter doesn't modify its parameters" );
357 # Correctly format dateexpiry
359 my $values = { dateexpiry => '2015-12-13', };
361 t::lib::Mocks::mock_preference('dateformat', 'metric');
362 t::lib::Mocks::mock_preference('timeformat', '24hr');
363 my $letter = C4::Letters::_parseletter({ content => "expiry on <<borrowers.dateexpiry>>"}, 'borrowers', $values);
364 is( $letter->{content}, 'expiry on 13/12/2015' );
366 t::lib::Mocks::mock_preference('dateformat', 'metric');
367 t::lib::Mocks::mock_preference('timeformat', '12hr');
368 $letter = C4::Letters::_parseletter({ content => "expiry on <<borrowers.dateexpiry>>"}, 'borrowers', $values);
369 is( $letter->{content}, 'expiry on 13/12/2015' );
372 my $bookseller = Koha::Acquisition::Bookseller->new(
375 address1 => "bookseller's address",
381 my $booksellerid = $bookseller->id;
383 Koha::Acquisition::Bookseller::Contact->new( { name => 'John Smith', phone => '0123456x1', claimacquisition => 1, orderacquisition => 1, booksellerid => $booksellerid } )->store;
384 Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues => 1, booksellerid => $booksellerid } )->store;
385 my $basketno = NewBasket($booksellerid, 1);
387 my $budgetid = C4::Budgets::AddBudget({
388 budget_code => "budget_code_test_letters",
389 budget_name => "budget_name_test_letters",
392 my $bib = MARC::Record->new();
393 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
395 MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
399 MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
403 my $logged_in_user = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $library->{branchcode} }});
404 t::lib::Mocks::mock_userenv({ patron => $logged_in_user });
406 ($biblionumber, $biblioitemnumber) = AddBiblio($bib, '');
407 my $order = Koha::Acquisition::Order->new(
409 basketno => $basketno,
411 biblionumber => $biblionumber,
412 budget_id => $budgetid,
415 my $ordernumber = $order->ordernumber;
417 C4::Acquisition::CloseBasket( $basketno );
420 $err = SendAlerts( 'claimacquisition', [ $ordernumber ], 'TESTACQCLAIM' ) }
421 qr/^Bookseller .* without emails at/,
422 "SendAlerts prints a warning";
423 is($err->{'error'}, 'no_email', "Trying to send an alert when there's no e-mail results in an error");
425 $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
426 $bookseller->contacts->next->email('testemail@mydomain.com')->store;
428 # Ensure that the preference 'LetterLog' is set to logging
429 t::lib::Mocks::mock_preference( 'LetterLog', 'on' );
431 # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
432 t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
436 $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ) }
438 "SendAlerts is using the mocked sendmail routine (orderacquisition)";
439 is($err, 1, "Successfully sent order.");
440 is($mail{'To'}, 'testemail@mydomain.com', "mailto correct in sent order");
441 is($mail{'Message'}, 'my vendor|John Smith|Ordernumber ' . $ordernumber . ' (Silence in the library) (1 ordered)', 'Order notice text constructed successfully');
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' ) }
455 "SendAlerts is using the mocked sendmail routine";
457 is($err, 1, "Successfully sent claim");
458 is($mail{'To'}, 'testemail@mydomain.com', "mailto correct in sent claim");
459 is($mail{'Message'}, 'my vendor|John Smith|Ordernumber ' . $ordernumber . ' (Silence in the library) (1 ordered)', 'Claim notice text constructed successfully');
466 my $internalnotes = 'intnotes';
467 $dbh->do(q|UPDATE subscription_numberpatterns SET numberingmethod='No. {X}' WHERE id=1|);
468 my $subscriptionid = NewSubscription(
469 undef, "", undef, undef, undef, $biblionumber,
470 '2013-01-01', 1, undef, undef, undef,
471 undef, undef, undef, undef, undef, undef,
472 1, $notes,undef, '2013-01-01', undef, 1,
473 undef, undef, 0, $internalnotes, 0,
474 undef, undef, 0, undef, '2013-12-31', 0
476 $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>>');});
477 my ($serials_count, @serials) = GetSerials($subscriptionid);
478 my $serial = $serials[0];
480 my $patron = Koha::Patron->new({
483 categorycode => $patron_category,
484 branchcode => $library->{branchcode},
485 dateofbirth => $date,
486 email => 'john.smith@test.de',
488 my $borrowernumber = $patron->borrowernumber;
489 my $subscription = Koha::Subscriptions->find( $subscriptionid );
490 $subscription->add_subscriber( $patron );
494 $err2 = SendAlerts( 'issue', $serial->{serialid}, 'RLIST' ) }
496 "SendAlerts is using the mocked sendmail routine";
497 is($err2, 1, "Successfully sent serial notification");
498 is($mail{'To'}, 'john.smith@test.de', "mailto correct in sent serial notification");
499 is($mail{'Message'}, 'Silence in the library,'.$subscriptionid.',No. 0', 'Serial notification text constructed successfully');
502 subtest 'SendAlerts - claimissue' => sub {
507 $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>>');});
509 my $bookseller = Koha::Acquisition::Bookseller->new(
512 address1 => "bookseller's address",
518 my $booksellerid = $bookseller->id;
520 Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues => 1, booksellerid => $booksellerid } )->store;
522 my $bib = MARC::Record->new();
523 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
525 MARC::Field->new('011', ' ', ' ', a => 'xxxx-yyyy'),
526 MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
530 MARC::Field->new('022', ' ', ' ', a => 'xxxx-yyyy'),
531 MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
534 my ($biblionumber) = AddBiblio($bib, '');
536 $dbh->do(q|UPDATE subscription_numberpatterns SET numberingmethod='No. {X}' WHERE id=1|);
537 my $subscriptionid = NewSubscription(
538 undef, "", $booksellerid, undef, undef, $biblionumber,
539 '2013-01-01', 1, undef, undef, undef,
540 undef, undef, undef, undef, undef, undef,
541 1, 'public',undef, '2013-01-01', undef, 1,
542 undef, undef, 0, 'internal', 0,
543 undef, undef, 0, undef, '2013-12-31', 0
546 my ($serials_count, @serials) = GetSerials($subscriptionid);
547 my @serialids = ($serials[0]->{serialid});
551 $err = SendAlerts( 'claimissues', \@serialids, 'TESTSERIALCLAIM' ) }
552 qr/^Bookseller .* without emails at/,
553 "Warn on vendor without email address";
555 $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
556 $bookseller->contacts->next->email('testemail@mydomain.com')->store;
558 # Ensure that the preference 'LetterLog' is set to logging
559 t::lib::Mocks::mock_preference( 'LetterLog', 'on' );
561 # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
562 t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
564 t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
568 $err = SendAlerts( 'claimissues', \@serialids , 'TESTSERIALCLAIM' ) }
570 "SendAlerts is using the mocked sendmail routine (claimissues)";
571 is($err, 1, "Successfully sent claim");
572 is($mail{'To'}, 'testemail@mydomain.com', "mailto correct in sent claim");
573 is($mail{'Message'}, "$serialids[0]|2013-01-01|Silence in the library|xxxx-yyyy", 'Serial claim letter for 1 issue constructed successfully');
577 my $publisheddate = output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 });
578 my $serialexpected = ( C4::Serials::findSerialsByStatus( 1, $subscriptionid ) )[0];
579 ModSerialStatus( $serials[0]->{serialid}, "No. 1", $publisheddate, $publisheddate, $publisheddate, '3', 'a note' );
580 ($serials_count, @serials) = GetSerials($subscriptionid);
581 push @serialids, ($serials[1]->{serialid});
583 $err = SendAlerts( 'claimissues', \@serialids , 'TESTSERIALCLAIM' );
584 is($mail{'Message'}, "$serialids[0]|2013-01-01|Silence in the library|xxxx-yyyy\n$serialids[1]|2013-01-01|Silence in the library|xxxx-yyyy", "Serial claim letter for 2 issues constructed successfully");
586 $dbh->do(q{DELETE FROM letter WHERE code = 'TESTSERIALCLAIM';});
588 $err = SendAlerts( 'orderacquisition', $basketno , 'TESTSERIALCLAIM' ) }
589 qr/No orderacquisition TESTSERIALCLAIM letter transported by email/,
590 "GetPreparedLetter warns about missing notice template";
591 is($err->{'error'}, 'no_letter', "No TESTSERIALCLAIM letter was defined");
596 subtest 'GetPreparedLetter' => sub {
599 Koha::Notice::Template->new(
604 message_transport_type => 'email'
609 $letter = C4::Letters::GetPreparedLetter(
611 letter_code => 'test',
614 qr{^ERROR: nothing to substitute},
615 'GetPreparedLetter should warn if tables, substiture and repeat are not set';
617 'No letter should be returned by GetPreparedLetter if something went wrong'
621 $letter = C4::Letters::GetPreparedLetter(
623 letter_code => 'test',
627 qr{^ERROR: nothing to substitute},
628 'GetPreparedLetter should warn if tables, substiture and repeat are not set, even if the key is passed';
630 'No letter should be returned by GetPreparedLetter if something went wrong'
637 subtest 'TranslateNotices' => sub {
640 t::lib::Mocks::mock_preference( 'TranslateNotices', '1' );
644 INSERT INTO letter (module, code, branchcode, name, title, content, message_transport_type, lang) VALUES
645 ('test', 'code', '', 'test', 'a test', 'just a test', 'email', 'default'),
646 ('test', 'code', '', 'test', 'una prueba', 'solo una prueba', 'email', 'es-ES');
649 my $letter = C4::Letters::GetPreparedLetter(
652 letter_code => 'code',
653 message_transport_type => 'email',
654 substitute => $substitute,
659 'GetPreparedLetter should return the default one if the lang parameter is not provided'
662 $letter = C4::Letters::GetPreparedLetter(
665 letter_code => 'code',
666 message_transport_type => 'email',
667 substitute => $substitute,
670 is( $letter->{title}, 'una prueba',
671 'GetPreparedLetter should return the required notice if it exists' );
673 $letter = C4::Letters::GetPreparedLetter(
676 letter_code => 'code',
677 message_transport_type => 'email',
678 substitute => $substitute,
684 'GetPreparedLetter should return the default notice if the one required does not exist'
687 t::lib::Mocks::mock_preference( 'TranslateNotices', '' );
689 $letter = C4::Letters::GetPreparedLetter(
692 letter_code => 'code',
693 message_transport_type => 'email',
694 substitute => $substitute,
697 is( $letter->{title}, 'a test',
698 'GetPreparedLetter should return the default notice if pref disabled but additional language exists' );
702 subtest 'SendQueuedMessages' => sub {
706 t::lib::Mocks::mock_preference( 'SMSSendDriver', 'Email' );
707 t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', '');
709 my $patron = Koha::Patrons->find($borrowernumber);
711 INSERT INTO message_queue(borrowernumber, subject, content, message_transport_type, status, letter_code)
712 VALUES (?, 'subject', 'content', 'sms', 'pending', 'just_a_code')
713 |, undef, $borrowernumber
715 eval { C4::Letters::SendQueuedMessages(); };
716 is( $@, '', 'SendQueuedMessages should not explode if the patron does not have a sms provider set' );
718 my $sms_pro = $builder->build_object({ class => 'Koha::SMS::Providers', value => { domain => 'kidclamp.rocks' } });
719 $patron->set( { smsalertnumber => '5555555555', sms_provider_id => $sms_pro->id() } )->store;
720 $message_id = C4::Letters::EnqueueLetter($my_message); #using datas set around line 95 and forward
721 C4::Letters::SendQueuedMessages();
723 my $message = $schema->resultset('MessageQueue')->search({
724 borrowernumber => $borrowernumber,
728 is( $message->to_address(), '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address not set' );
730 $message->from_address(),
732 'SendQueuedMessages uses message queue item \"from address\" for SMS by email when EmailSMSSendDriverFromAddress system preference is not set'
735 $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber, status => 'sent'})->delete(); #clear borrower queue
737 t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', 'override@example.com');
739 $message_id = C4::Letters::EnqueueLetter($my_message);
740 C4::Letters::SendQueuedMessages();
742 $message = $schema->resultset('MessageQueue')->search({
743 borrowernumber => $borrowernumber,
748 $message->from_address(),
749 'override@example.com',
750 'SendQueuedMessages uses EmailSMSSendDriverFromAddress value for SMS by email when EmailSMSSendDriverFromAddress is set'
753 $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber,status => 'sent'})->delete(); #clear borrower queue
754 $my_message->{to_address} = 'fixme@kidclamp.iswrong';
755 $message_id = C4::Letters::EnqueueLetter($my_message);
757 my $number_attempted = C4::Letters::SendQueuedMessages({
758 borrowernumber => -1, # -1 still triggers the borrowernumber condition
759 letter_code => 'PASSWORD_RESET',
761 is ( $number_attempted, 0, 'There were no password reset messages for SendQueuedMessages to attempt.' );
763 C4::Letters::SendQueuedMessages();
764 my $sms_message_address = $schema->resultset('MessageQueue')->search({
765 borrowernumber => $borrowernumber,
767 })->next()->to_address();
768 is( $sms_message_address, '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address is set incorrectly' );
772 subtest 'get_item_content' => sub {
775 t::lib::Mocks::mock_preference('dateformat', 'metric');
776 t::lib::Mocks::mock_preference('timeformat', '24hr');
778 {date_due => '2041-01-01 12:34', title => 'a first title', barcode => 'a_first_barcode', author => 'a_first_author', itemnumber => 1 },
779 {date_due => '2042-01-02 23:45', title => 'a second title', barcode => 'a_second_barcode', author => 'a_second_author', itemnumber => 2 },
781 my @item_content_fields = qw( date_due title barcode author itemnumber );
784 for my $item ( @items ) {
785 $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields } );
788 my $expected_items_content = <<EOF;
789 01/01/2041 12:34\ta first title\ta_first_barcode\ta_first_author\t1
790 02/01/2042 23:45\ta second title\ta_second_barcode\ta_second_author\t2
792 is( $items_content, $expected_items_content, 'get_item_content should return correct items info with time (default)' );
795 $items_content = q||;
796 for my $item ( @items ) {
797 $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields, dateonly => 1, } );
800 $expected_items_content = <<EOF;
801 01/01/2041\ta first title\ta_first_barcode\ta_first_author\t1
802 02/01/2042\ta second title\ta_second_barcode\ta_second_author\t2
804 is( $items_content, $expected_items_content, 'get_item_content should return correct items info without time (if dateonly => 1)' );
807 subtest 'Test limit parameter for SendQueuedMessages' => sub {
810 my $dbh = C4::Context->dbh;
812 my $borrowernumber = Koha::Patron->new({
815 categorycode => $patron_category,
816 branchcode => $library->{branchcode},
817 dateofbirth => $date,
818 smsalertnumber => undef,
819 })->store->borrowernumber;
821 $dbh->do(q|DELETE FROM message_queue|);
824 'content' => 'a message',
825 'metadata' => 'metadata',
826 'code' => 'TEST_MESSAGE',
827 'content_type' => 'text/plain',
828 'title' => 'message title'
830 'borrowernumber' => $borrowernumber,
831 'to_address' => undef,
832 'message_transport_type' => 'sms',
833 'from_address' => 'from@example.com'
835 C4::Letters::EnqueueLetter($my_message);
836 C4::Letters::EnqueueLetter($my_message);
837 C4::Letters::EnqueueLetter($my_message);
838 C4::Letters::EnqueueLetter($my_message);
839 C4::Letters::EnqueueLetter($my_message);
840 my $messages_processed = C4::Letters::SendQueuedMessages( { limit => 1 } );
841 is( $messages_processed, 1,
842 'Processed 1 message with limit of 1 and 5 unprocessed messages' );
843 $messages_processed = C4::Letters::SendQueuedMessages( { limit => 2 } );
844 is( $messages_processed, 2,
845 'Processed 2 message with limit of 2 and 4 unprocessed messages' );
846 $messages_processed = C4::Letters::SendQueuedMessages( { limit => 3 } );
847 is( $messages_processed, 2,
848 'Processed 2 message with limit of 3 and 2 unprocessed messages' );