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