Bug 18990: Overdue Notices are not sending through SMS correctly
[koha.git] / t / db_dependent / Letters.t
1 #!/usr/bin/perl
2
3 # This file is part of Koha.
4 #
5 # Copyright (C) 2013 Equinox Software, Inc.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use Modern::Perl;
21 use Test::More tests => 77;
22 use Test::MockModule;
23 use Test::Warn;
24
25 use MARC::Record;
26
27 my %mail;
28 my $module = new Test::MockModule('Mail::Sendmail');
29 $module->mock(
30     'sendmail',
31     sub {
32         warn "Fake sendmail";
33         %mail = @_;
34     }
35 );
36
37 use_ok('C4::Context');
38 use_ok('C4::Members');
39 use_ok('C4::Acquisition');
40 use_ok('C4::Biblio');
41 use_ok('C4::Letters');
42 use t::lib::Mocks;
43 use t::lib::TestBuilder;
44 use Koha::Database;
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;
49 use Koha::Libraries;
50 use Koha::Notice::Templates;
51 my $schema = Koha::Database->schema;
52 $schema->storage->txn_begin();
53
54 my $builder = t::lib::TestBuilder->new;
55 my $dbh = C4::Context->dbh;
56 $dbh->{RaiseError} = 1;
57
58 $dbh->do(q|DELETE FROM letter|);
59 $dbh->do(q|DELETE FROM message_queue|);
60 $dbh->do(q|DELETE FROM message_transport_types|);
61
62 my $library = $builder->build({
63     source => 'Branch',
64 });
65 my $patron_category = $builder->build({ source => 'Category' })->{categorycode};
66 my $date = dt_from_string;
67 my $borrowernumber = AddMember(
68     firstname    => 'Jane',
69     surname      => 'Smith',
70     categorycode => $patron_category,
71     branchcode   => $library->{branchcode},
72     dateofbirth  => $date,
73     smsalertnumber => undef,
74 );
75
76 my $marc_record = MARC::Record->new;
77 my( $biblionumber, $biblioitemnumber ) = AddBiblio( $marc_record, '' );
78
79
80
81 # GetMessageTransportTypes
82 my $mtts = C4::Letters::GetMessageTransportTypes();
83 is( @$mtts, 0, 'GetMessageTransportTypes returns the correct number of message types' );
84
85 $dbh->do(q|
86     INSERT INTO message_transport_types( message_transport_type ) VALUES ('email'), ('phone'), ('print'), ('sms')
87 |);
88 $mtts = C4::Letters::GetMessageTransportTypes();
89 is_deeply( $mtts, ['email', 'phone', 'print', 'sms'], 'GetMessageTransportTypes returns all values' );
90
91
92 # EnqueueLetter
93 is( C4::Letters::EnqueueLetter(), undef, 'EnqueueLetter without argument returns undef' );
94
95 my $my_message = {
96     borrowernumber         => $borrowernumber,
97     message_transport_type => 'sms',
98     to_address             => undef,
99     from_address           => 'from@example.com',
100 };
101 my $message_id = C4::Letters::EnqueueLetter($my_message);
102 is( $message_id, undef, 'EnqueueLetter without the letter argument returns undef' );
103
104 delete $my_message->{message_transport_type};
105 $my_message->{letter} = {
106     content      => 'a message',
107     title        => 'message title',
108     metadata     => 'metadata',
109     code         => 'TEST_MESSAGE',
110     content_type => 'text/plain',
111 };
112
113 $message_id = C4::Letters::EnqueueLetter($my_message);
114 is( $message_id, undef, 'EnqueueLetter without the message type argument argument returns undef' );
115
116 $my_message->{message_transport_type} = 'sms';
117 $message_id = C4::Letters::EnqueueLetter($my_message);
118 ok(defined $message_id && $message_id > 0, 'new message successfully queued');
119
120
121 # GetQueuedMessages
122 my $messages = C4::Letters::GetQueuedMessages();
123 is( @$messages, 1, 'GetQueuedMessages without argument returns all the entries' );
124
125 $messages = C4::Letters::GetQueuedMessages({ borrowernumber => $borrowernumber });
126 is( @$messages, 1, 'one message stored for the borrower' );
127 is( $messages->[0]->{message_id}, $message_id, 'EnqueueLetter returns the message id correctly' );
128 is( $messages->[0]->{borrowernumber}, $borrowernumber, 'EnqueueLetter stores the borrower number correctly' );
129 is( $messages->[0]->{subject}, $my_message->{letter}->{title}, 'EnqueueLetter stores the subject correctly' );
130 is( $messages->[0]->{content}, $my_message->{letter}->{content}, 'EnqueueLetter stores the content correctly' );
131 is( $messages->[0]->{message_transport_type}, $my_message->{message_transport_type}, 'EnqueueLetter stores the message type correctly' );
132 is( $messages->[0]->{status}, 'pending', 'EnqueueLetter stores the status pending correctly' );
133
134
135 # SendQueuedMessages
136 my $messages_processed = C4::Letters::SendQueuedMessages();
137 is($messages_processed, 1, 'all queued messages processed');
138 $messages = C4::Letters::GetQueuedMessages({ borrowernumber => $borrowernumber });
139 is(
140     $messages->[0]->{status},
141     'failed',
142     'message marked failed if tried to send SMS message for borrower with no smsalertnumber set (bug 11208)'
143 );
144
145 # ResendMessage
146 my $resent = C4::Letters::ResendMessage($messages->[0]->{message_id});
147 my $message = C4::Letters::GetMessage( $messages->[0]->{message_id});
148 is( $resent, 1, 'The message should have been resent' );
149 is($message->{status},'pending', 'ResendMessage sets status to pending correctly (bug 12426)');
150 $resent = C4::Letters::ResendMessage($messages->[0]->{message_id});
151 is( $resent, 0, 'The message should not have been resent again' );
152 $resent = C4::Letters::ResendMessage();
153 is( $resent, undef, 'ResendMessage should return undef if not message_id given' );
154
155 # GetLetters
156 my $letters = C4::Letters::GetLetters();
157 is( @$letters, 0, 'GetLetters returns the correct number of letters' );
158
159 my $title = q|<<branches.branchname>> - <<status>>|;
160 my $content = q{Dear <<borrowers.firstname>> <<borrowers.surname>>,
161 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.
162
163 <<branches.branchname>>
164 <<branches.branchaddress1>>
165 URL: <<OPACBaseURL>>
166
167 The following item(s) is/are currently <<status>>:
168
169 <item> <<count>>. <<items.itemcallnumber>>, Barcode: <<items.barcode>> </item>
170
171 Thank-you for your prompt attention to this matter.
172 Don't forget your date of birth: <<borrowers.dateofbirth>>.
173 Look at this wonderful biblio timestamp: <<biblio.timestamp>>.
174 };
175
176 $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 );
177 $letters = C4::Letters::GetLetters();
178 is( @$letters, 1, 'GetLetters returns the correct number of letters' );
179 is( $letters->[0]->{branchcode}, $library->{branchcode}, 'GetLetters gets the branch code correctly' );
180 is( $letters->[0]->{module}, 'my module', 'GetLetters gets the module correctly' );
181 is( $letters->[0]->{code}, 'my code', 'GetLetters gets the code correctly' );
182 is( $letters->[0]->{name}, 'my name', 'GetLetters gets the name correctly' );
183
184
185 # getletter
186 subtest 'getletter' => sub {
187     plan tests => 16;
188     t::lib::Mocks::mock_preference('IndependentBranches', 0);
189     my $letter = C4::Letters::getletter('my module', 'my code', $library->{branchcode}, 'email');
190     is( $letter->{branchcode}, $library->{branchcode}, 'GetLetters gets the branch code correctly' );
191     is( $letter->{module}, 'my module', 'GetLetters gets the module correctly' );
192     is( $letter->{code}, 'my code', 'GetLetters gets the code correctly' );
193     is( $letter->{name}, 'my name', 'GetLetters gets the name correctly' );
194     is( $letter->{is_html}, 1, 'GetLetters gets the boolean is_html correctly' );
195     is( $letter->{title}, $title, 'GetLetters gets the title correctly' );
196     is( $letter->{content}, $content, 'GetLetters gets the content correctly' );
197     is( $letter->{message_transport_type}, 'email', 'GetLetters gets the message type correctly' );
198
199     my $context = Test::MockModule->new('C4::Context');
200     $context->mock( 'userenv', sub {
201         return {
202             flags  => 1,
203             branch => "anotherlib" }
204     });
205
206     t::lib::Mocks::mock_preference('IndependentBranches', 1);
207     $letter = C4::Letters::getletter('my module', 'my code', $library->{branchcode}, 'email');
208     is( $letter->{branchcode}, $library->{branchcode}, 'GetLetters gets the branch code correctly' );
209     is( $letter->{module}, 'my module', 'GetLetters gets the module correctly' );
210     is( $letter->{code}, 'my code', 'GetLetters gets the code correctly' );
211     is( $letter->{name}, 'my name', 'GetLetters gets the name correctly' );
212     is( $letter->{is_html}, 1, 'GetLetters gets the boolean is_html correctly' );
213     is( $letter->{title}, $title, 'GetLetters gets the title correctly' );
214     is( $letter->{content}, $content, 'GetLetters gets the content correctly' );
215     is( $letter->{message_transport_type}, 'email', 'GetLetters gets the message type correctly' );
216
217     $context->unmock('userenv');
218 };
219
220
221
222 # Regression test for Bug 14206
223 $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 );
224 my $letter14206_a = C4::Letters::getletter('my module', 'my code', 'FFL' );
225 is( $letter14206_a->{message_transport_type}, 'print', 'Bug 14206 - message_transport_type not passed, correct mtt detected' );
226 my $letter14206_b = C4::Letters::getletter('my module', 'my code', 'FFL', 'print');
227 is( $letter14206_b->{message_transport_type}, 'print', 'Bug 14206 - message_transport_type passed, correct mtt detected'  );
228
229 # test for overdue_notices.pl
230 my $overdue_rules = {
231     letter1         => 'my code',
232 };
233 my $i = 1;
234 my $branchcode = 'FFL';
235 my $letter14206_c = C4::Letters::getletter('my module', $overdue_rules->{"letter$i"}, $branchcode);
236 is( $letter14206_c->{message_transport_type}, 'print', 'Bug 14206 - correct mtt detected for call from overdue_notices.pl' );
237
238 # addalert
239 my $type = 'my type';
240 my $externalid = 'my external id';
241 my $alert_id = C4::Letters::addalert($borrowernumber, $type, $externalid);
242 isnt( $alert_id, undef, 'addalert does not return undef' );
243
244
245 # getalert
246 my $alerts = C4::Letters::getalert();
247 is( @$alerts, 1, 'getalert should not fail without parameter' );
248 $alerts = C4::Letters::getalert($borrowernumber);
249 is( @$alerts, 1, 'addalert adds an alert' );
250 is( $alerts->[0]->{alertid}, $alert_id, 'addalert returns the alert id correctly' );
251 is( $alerts->[0]->{type}, $type, 'addalert stores the type correctly' );
252 is( $alerts->[0]->{externalid}, $externalid, 'addalert stores the externalid correctly' );
253
254 $alerts = C4::Letters::getalert($borrowernumber, $type);
255 is( @$alerts, 1, 'getalert returns the correct number of alerts' );
256 $alerts = C4::Letters::getalert($borrowernumber, $type, $externalid);
257 is( @$alerts, 1, 'getalert returns the correct number of alerts' );
258 $alerts = C4::Letters::getalert($borrowernumber, 'another type');
259 is( @$alerts, 0, 'getalert returns the correct number of alerts' );
260 $alerts = C4::Letters::getalert($borrowernumber, $type, 'another external id');
261 is( @$alerts, 0, 'getalert returns the correct number of alerts' );
262
263
264 # delalert
265 eval {
266     C4::Letters::delalert();
267 };
268 isnt( $@, undef, 'delalert without argument returns an error' );
269 $alerts = C4::Letters::getalert($borrowernumber);
270 is( @$alerts, 1, 'delalert without argument does not remove an alert' );
271
272 C4::Letters::delalert($alert_id);
273 $alerts = C4::Letters::getalert($borrowernumber);
274 is( @$alerts, 0, 'delalert removes an alert' );
275
276
277 # GetPreparedLetter
278 t::lib::Mocks::mock_preference('OPACBaseURL', 'http://thisisatest.com');
279
280 my $sms_content = 'This is a SMS for an <<status>>';
281 $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 );
282
283 my $tables = {
284     borrowers => $borrowernumber,
285     branches => $library->{branchcode},
286     biblio => $biblionumber,
287 };
288 my $substitute = {
289     status => 'overdue',
290 };
291 my $repeat = [
292     {
293         itemcallnumber => 'my callnumber1',
294         barcode        => '1234',
295     },
296     {
297         itemcallnumber => 'my callnumber2',
298         barcode        => '5678',
299     },
300 ];
301 my $prepared_letter = GetPreparedLetter((
302     module      => 'my module',
303     branchcode  => $library->{branchcode},
304     letter_code => 'my code',
305     tables      => $tables,
306     substitute  => $substitute,
307     repeat      => $repeat,
308 ));
309 my $retrieved_library = Koha::Libraries->find($library->{branchcode});
310 my $my_title_letter = $retrieved_library->branchname . qq| - $substitute->{status}|;
311 my $biblio_timestamp = dt_from_string( GetBiblioData($biblionumber)->{timestamp} );
312 my $my_content_letter = qq|Dear Jane Smith,
313 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.
314
315 |.$retrieved_library->branchname.qq|
316 |.$retrieved_library->branchaddress1.qq|
317 URL: http://thisisatest.com
318
319 The following item(s) is/are currently $substitute->{status}:
320
321 <item> 1. $repeat->[0]->{itemcallnumber}, Barcode: $repeat->[0]->{barcode} </item>
322 <item> 2. $repeat->[1]->{itemcallnumber}, Barcode: $repeat->[1]->{barcode} </item>
323
324 Thank-you for your prompt attention to this matter.
325 Don't forget your date of birth: | . output_pref({ dt => $date, dateonly => 1 }) . q|.
326 Look at this wonderful biblio timestamp: | . output_pref({ dt => $biblio_timestamp })  . ".\n";
327
328 is( $prepared_letter->{title}, $my_title_letter, 'GetPreparedLetter returns the title correctly' );
329 is( $prepared_letter->{content}, $my_content_letter, 'GetPreparedLetter returns the content correctly' );
330
331 $prepared_letter = GetPreparedLetter((
332     module                 => 'my module',
333     branchcode             => $library->{branchcode},
334     letter_code            => 'my code',
335     tables                 => $tables,
336     substitute             => $substitute,
337     repeat                 => $repeat,
338     message_transport_type => 'sms',
339 ));
340 $my_content_letter = qq|This is a SMS for an $substitute->{status}|;
341 is( $prepared_letter->{content}, $my_content_letter, 'GetPreparedLetter returns the content correctly' );
342
343 $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>>.');});
344 $prepared_letter = GetPreparedLetter((
345     module                 => 'test_date',
346     branchcode             => '',
347     letter_code            => 'test_date',
348     tables                 => $tables,
349     substitute             => $substitute,
350     repeat                 => $repeat,
351 ));
352 is( $prepared_letter->{content}, q|This one only contains the date: | . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 1' );
353
354 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp | dateonly>>.' WHERE code = 'test_date';});
355 $prepared_letter = GetPreparedLetter((
356     module                 => 'test_date',
357     branchcode             => '',
358     letter_code            => 'test_date',
359     tables                 => $tables,
360     substitute             => $substitute,
361     repeat                 => $repeat,
362 ));
363 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 2' );
364
365 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp|dateonly >>.' WHERE code = 'test_date';});
366 $prepared_letter = GetPreparedLetter((
367     module                 => 'test_date',
368     branchcode             => '',
369     letter_code            => 'test_date',
370     tables                 => $tables,
371     substitute             => $substitute,
372     repeat                 => $repeat,
373 ));
374 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 3' );
375
376 t::lib::Mocks::mock_preference( 'TimeFormat', '12hr' );
377 my $yesterday_night = $date->clone->add( days => -1 )->set_hour(22);
378 $dbh->do(q|UPDATE biblio SET timestamp = ? WHERE biblionumber = ?|, undef, $yesterday_night, $biblionumber );
379 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp>>.' WHERE code = 'test_date';});
380 $prepared_letter = GetPreparedLetter((
381     module                 => 'test_date',
382     branchcode             => '',
383     letter_code            => 'test_date',
384     tables                 => $tables,
385     substitute             => $substitute,
386     repeat                 => $repeat,
387 ));
388 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $yesterday_night }) . q|.|, 'dateonly test 3' );
389
390 $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>');});
391 $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>');});
392
393 # Test that _parseletter doesn't modify its parameters bug 15429
394 {
395     my $values = { dateexpiry => '2015-12-13', };
396     C4::Letters::_parseletter($prepared_letter, 'borrowers', $values);
397     is( $values->{dateexpiry}, '2015-12-13', "_parseletter doesn't modify its parameters" );
398 }
399
400 my $bookseller = Koha::Acquisition::Bookseller->new(
401     {
402         name => "my vendor",
403         address1 => "bookseller's address",
404         phone => "0123456",
405         active => 1,
406         deliverytime => 5,
407     }
408 )->store;
409 my $booksellerid = $bookseller->id;
410
411 Koha::Acquisition::Bookseller::Contact->new( { name => 'John Smith',  phone => '0123456x1', claimacquisition => 1, orderacquisition => 1, booksellerid => $booksellerid } )->store;
412 Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues      => 1, booksellerid => $booksellerid } )->store;
413 my $basketno = NewBasket($booksellerid, 1);
414
415 my $budgetid = C4::Budgets::AddBudget({
416     budget_code => "budget_code_test_letters",
417     budget_name => "budget_name_test_letters",
418 });
419
420 my $bib = MARC::Record->new();
421 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
422     $bib->append_fields(
423         MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
424     );
425 } else {
426     $bib->append_fields(
427         MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
428     );
429 }
430
431 ($biblionumber, $biblioitemnumber) = AddBiblio($bib, '');
432 my $order = Koha::Acquisition::Order->new(
433     {
434         basketno => $basketno,
435         quantity => 1,
436         biblionumber => $biblionumber,
437         budget_id => $budgetid,
438     }
439 )->store;
440 my $ordernumber = $order->ordernumber;
441
442 C4::Acquisition::CloseBasket( $basketno );
443 my $err;
444 warning_like {
445     $err = SendAlerts( 'claimacquisition', [ $ordernumber ], 'TESTACQCLAIM' ) }
446     qr/^Bookseller .* without emails at/,
447     "SendAlerts prints a warning";
448 is($err->{'error'}, 'no_email', "Trying to send an alert when there's no e-mail results in an error");
449
450 $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
451 $bookseller->contacts->next->email('testemail@mydomain.com')->store;
452
453 # Ensure that the preference 'LetterLog' is set to logging
454 t::lib::Mocks::mock_preference( 'LetterLog', 'on' );
455
456 # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
457 C4::Context->_new_userenv('DUMMY');
458 C4::Context->set_userenv( 0, 0, 0, 'firstname', 'surname', $library->{branchcode}, 'My Library', 0, '', '');
459 t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
460
461 {
462 warning_is {
463     $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ) }
464     "Fake sendmail",
465     "SendAlerts is using the mocked sendmail routine (orderacquisition)";
466 is($err, 1, "Successfully sent order.");
467 is($mail{'To'}, 'testemail@mydomain.com', "mailto correct in sent order");
468 is($mail{'Message'}, 'my vendor|John Smith|Ordernumber ' . $ordernumber . ' (Silence in the library) (1 ordered)', 'Order notice text constructed successfully');
469
470 $dbh->do(q{DELETE FROM letter WHERE code = 'TESTACQORDER';});
471 warning_like {
472     $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ) }
473     qr/No orderacquisition TESTACQORDER letter transported by email/,
474     "GetPreparedLetter warns about missing notice template";
475 is($err->{'error'}, 'no_letter', "No TESTACQORDER letter was defined.");
476 }
477
478 {
479 warning_is {
480     $err = SendAlerts( 'claimacquisition', [ $ordernumber ], 'TESTACQCLAIM' ) }
481     "Fake sendmail",
482     "SendAlerts is using the mocked sendmail routine";
483
484 is($err, 1, "Successfully sent claim");
485 is($mail{'To'}, 'testemail@mydomain.com', "mailto correct in sent claim");
486 is($mail{'Message'}, 'my vendor|John Smith|Ordernumber ' . $ordernumber . ' (Silence in the library) (1 ordered)', 'Claim notice text constructed successfully');
487 }
488
489 {
490 use C4::Serials;
491
492 my $notes = 'notes';
493 my $internalnotes = 'intnotes';
494 $dbh->do(q|UPDATE subscription_numberpatterns SET numberingmethod='No. {X}' WHERE id=1|);
495 my $subscriptionid = NewSubscription(
496      undef,      "",     undef, undef, undef, $biblionumber,
497     '2013-01-01', 1, undef, undef,  undef,
498     undef,      undef,  undef, undef, undef, undef,
499     1,          $notes,undef, '2013-01-01', undef, 1,
500     undef,       undef,  0,    $internalnotes,  0,
501     undef, undef, 0,          undef,         '2013-12-31', 0
502 );
503 $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>>');});
504 my ($serials_count, @serials) = GetSerials($subscriptionid);
505 my $serial = $serials[0];
506
507 my $borrowernumber = AddMember(
508     firstname    => 'John',
509     surname      => 'Smith',
510     categorycode => $patron_category,
511     branchcode   => $library->{branchcode},
512     dateofbirth  => $date,
513     email        => 'john.smith@test.de',
514 );
515 my $alert_id = C4::Letters::addalert($borrowernumber, 'issue', $subscriptionid);
516
517
518 my $err2;
519 warning_is {
520 $err2 = SendAlerts( 'issue', $serial->{serialid}, 'RLIST' ) }
521     "Fake sendmail",
522     "SendAlerts is using the mocked sendmail routine";
523 is($err2, 1, "Successfully sent serial notification");
524 is($mail{'To'}, 'john.smith@test.de', "mailto correct in sent serial notification");
525 is($mail{'Message'}, 'Silence in the library,'.$subscriptionid.',No. 0', 'Serial notification text constructed successfully');
526 }
527
528 subtest 'GetPreparedLetter' => sub {
529     plan tests => 4;
530
531     Koha::Notice::Template->new(
532         {
533             module                 => 'test',
534             code                   => 'test',
535             branchcode             => '',
536             message_transport_type => 'email'
537         }
538     )->store;
539     my $letter;
540     warning_like {
541         $letter = C4::Letters::GetPreparedLetter(
542             module      => 'test',
543             letter_code => 'test',
544         );
545     }
546     qr{^ERROR: nothing to substitute},
547 'GetPreparedLetter should warn if tables, substiture and repeat are not set';
548     is( $letter, undef,
549 'No letter should be returned by GetPreparedLetter if something went wrong'
550     );
551
552     warning_like {
553         $letter = C4::Letters::GetPreparedLetter(
554             module      => 'test',
555             letter_code => 'test',
556             substitute  => {}
557         );
558     }
559     qr{^ERROR: nothing to substitute},
560 'GetPreparedLetter should warn if tables, substiture and repeat are not set, even if the key is passed';
561     is( $letter, undef,
562 'No letter should be returned by GetPreparedLetter if something went wrong'
563     );
564
565 };
566
567
568
569 subtest 'TranslateNotices' => sub {
570     plan tests => 4;
571
572     t::lib::Mocks::mock_preference( 'TranslateNotices', '1' );
573
574     $dbh->do(
575         q|
576         INSERT INTO letter (module, code, branchcode, name, title, content, message_transport_type, lang) VALUES
577         ('test', 'code', '', 'test', 'a test', 'just a test', 'email', 'default'),
578         ('test', 'code', '', 'test', 'una prueba', 'solo una prueba', 'email', 'es-ES');
579     | );
580     my $substitute = {};
581     my $letter = C4::Letters::GetPreparedLetter(
582             module                 => 'test',
583             tables                 => $tables,
584             letter_code            => 'code',
585             message_transport_type => 'email',
586             substitute             => $substitute,
587     );
588     is(
589         $letter->{title},
590         'a test',
591         'GetPreparedLetter should return the default one if the lang parameter is not provided'
592     );
593
594     $letter = C4::Letters::GetPreparedLetter(
595             module                 => 'test',
596             tables                 => $tables,
597             letter_code            => 'code',
598             message_transport_type => 'email',
599             substitute             => $substitute,
600             lang                   => 'es-ES',
601     );
602     is( $letter->{title}, 'una prueba',
603         'GetPreparedLetter should return the required notice if it exists' );
604
605     $letter = C4::Letters::GetPreparedLetter(
606             module                 => 'test',
607             tables                 => $tables,
608             letter_code            => 'code',
609             message_transport_type => 'email',
610             substitute             => $substitute,
611             lang                   => 'fr-FR',
612     );
613     is(
614         $letter->{title},
615         'a test',
616         'GetPreparedLetter should return the default notice if the one required does not exist'
617     );
618
619     t::lib::Mocks::mock_preference( 'TranslateNotices', '' );
620
621     $letter = C4::Letters::GetPreparedLetter(
622             module                 => 'test',
623             tables                 => $tables,
624             letter_code            => 'code',
625             message_transport_type => 'email',
626             substitute             => $substitute,
627             lang                   => 'es-ES',
628     );
629     is( $letter->{title}, 'a test',
630         'GetPreparedLetter should return the default notice if pref disabled but additional language exists' );
631
632 };
633
634 subtest 'SendQueuedMessages' => sub {
635
636     plan tests => 3;
637     t::lib::Mocks::mock_preference( 'SMSSendDriver', 'Email' );
638     my $patron = Koha::Patrons->find($borrowernumber);
639     $dbh->do(q|
640         INSERT INTO message_queue(borrowernumber, subject, content, message_transport_type, status, letter_code)
641         VALUES (?, 'subject', 'content', 'sms', 'pending', 'just_a_code')
642         |, undef, $borrowernumber
643     );
644     eval { C4::Letters::SendQueuedMessages(); };
645     is( $@, '', 'SendQueuedMessages should not explode if the patron does not have a sms provider set' );
646
647     my $sms_pro = $builder->build_object({ class => 'Koha::SMS::Providers', value => { domain => 'kidclamp.rocks' } });
648     ModMember( borrowernumber => $borrowernumber, smsalertnumber => '5555555555', sms_provider_id => $sms_pro->id() );
649     $message_id = C4::Letters::EnqueueLetter($my_message); #using datas set around line 95 and forward
650     C4::Letters::SendQueuedMessages();
651     my $sms_message_address = $schema->resultset('MessageQueue')->search({
652         borrowernumber => $borrowernumber,
653         status => 'sent'
654     })->next()->to_address();
655     is( $sms_message_address, '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address not set' );
656     $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber,status => 'sent'})->delete(); #clear borrower queue
657     $my_message->{to_address} = 'fixme@kidclamp.iswrong';
658     $message_id = C4::Letters::EnqueueLetter($my_message);
659     C4::Letters::SendQueuedMessages();
660     $sms_message_address = $schema->resultset('MessageQueue')->search({
661         borrowernumber => $borrowernumber,
662         status => 'sent'
663     })->next()->to_address();
664     is( $sms_message_address, '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address is set incorrectly' );
665
666 };
667
668 subtest 'get_item_content' => sub {
669     plan tests => 2;
670
671     t::lib::Mocks::mock_preference('dateformat', 'metric');
672     t::lib::Mocks::mock_preference('timeformat', '24hr');
673     my @items = (
674         {date_due => '2041-01-01 12:34', title => 'a first title', barcode => 'a_first_barcode', author => 'a_first_author', itemnumber => 1 },
675         {date_due => '2042-01-02 23:45', title => 'a second title', barcode => 'a_second_barcode', author => 'a_second_author', itemnumber => 2 },
676     );
677     my @item_content_fields = qw( date_due title barcode author itemnumber );
678
679     my $items_content;
680     for my $item ( @items ) {
681         $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields } );
682     }
683
684     my $expected_items_content = <<EOF;
685 01/01/2041 12:34\ta first title\ta_first_barcode\ta_first_author\t1
686 02/01/2042 23:45\ta second title\ta_second_barcode\ta_second_author\t2
687 EOF
688     is( $items_content, $expected_items_content, 'get_item_content should return correct items info with time (default)' );
689
690
691     $items_content = q||;
692     for my $item ( @items ) {
693         $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields, dateonly => 1, } );
694     }
695
696     $expected_items_content = <<EOF;
697 01/01/2041\ta first title\ta_first_barcode\ta_first_author\t1
698 02/01/2042\ta second title\ta_second_barcode\ta_second_author\t2
699 EOF
700     is( $items_content, $expected_items_content, 'get_item_content should return correct items info without time (if dateonly => 1)' );
701 };
702
703 subtest 'Test limit parameter for SendQueuedMessages' => sub {
704     plan tests => 3;
705
706     my $dbh = C4::Context->dbh;
707
708     my $borrowernumber = AddMember(
709         firstname    => 'Jane',
710         surname      => 'Smith',
711         categorycode => $patron_category,
712         branchcode   => $library->{branchcode},
713         dateofbirth  => $date,
714         smsalertnumber => undef,
715     );
716
717     $dbh->do(q|DELETE FROM message_queue|);
718     $my_message = {
719         'letter' => {
720             'content'      => 'a message',
721             'metadata'     => 'metadata',
722             'code'         => 'TEST_MESSAGE',
723             'content_type' => 'text/plain',
724             'title'        => 'message title'
725         },
726         'borrowernumber'         => $borrowernumber,
727         'to_address'             => undef,
728         'message_transport_type' => 'sms',
729         'from_address'           => 'from@example.com'
730     };
731     C4::Letters::EnqueueLetter($my_message);
732     C4::Letters::EnqueueLetter($my_message);
733     C4::Letters::EnqueueLetter($my_message);
734     C4::Letters::EnqueueLetter($my_message);
735     C4::Letters::EnqueueLetter($my_message);
736     my $messages_processed = C4::Letters::SendQueuedMessages( { limit => 1 } );
737     is( $messages_processed, 1,
738         'Processed 1 message with limit of 1 and 5 unprocessed messages' );
739     $messages_processed = C4::Letters::SendQueuedMessages( { limit => 2 } );
740     is( $messages_processed, 2,
741         'Processed 2 message with limit of 2 and 4 unprocessed messages' );
742     $messages_processed = C4::Letters::SendQueuedMessages( { limit => 3 } );
743     is( $messages_processed, 2,
744         'Processed 2 message with limit of 3 and 2 unprocessed messages' );
745 };