Bug 21953: (follow-up) Fix test count
[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 => 67;
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 use Koha::Patrons;
52 use Koha::Subscriptions;
53 my $schema = Koha::Database->schema;
54 $schema->storage->txn_begin();
55
56 my $builder = t::lib::TestBuilder->new;
57 my $dbh = C4::Context->dbh;
58 $dbh->{RaiseError} = 1;
59
60 $dbh->do(q|DELETE FROM letter|);
61 $dbh->do(q|DELETE FROM message_queue|);
62 $dbh->do(q|DELETE FROM message_transport_types|);
63
64 my $library = $builder->build({
65     source => 'Branch',
66 });
67 my $patron_category = $builder->build({ source => 'Category' })->{categorycode};
68 my $date = dt_from_string;
69 my $borrowernumber = Koha::Patron->new({
70     firstname    => 'Jane',
71     surname      => 'Smith',
72     categorycode => $patron_category,
73     branchcode   => $library->{branchcode},
74     dateofbirth  => $date,
75     smsalertnumber => undef,
76 })->store->borrowernumber;
77
78 my $marc_record = MARC::Record->new;
79 my( $biblionumber, $biblioitemnumber ) = AddBiblio( $marc_record, '' );
80
81
82
83 # GetMessageTransportTypes
84 my $mtts = C4::Letters::GetMessageTransportTypes();
85 is( @$mtts, 0, 'GetMessageTransportTypes returns the correct number of message types' );
86
87 $dbh->do(q|
88     INSERT INTO message_transport_types( message_transport_type ) VALUES ('email'), ('phone'), ('print'), ('sms')
89 |);
90 $mtts = C4::Letters::GetMessageTransportTypes();
91 is_deeply( $mtts, ['email', 'phone', 'print', 'sms'], 'GetMessageTransportTypes returns all values' );
92
93
94 # EnqueueLetter
95 is( C4::Letters::EnqueueLetter(), undef, 'EnqueueLetter without argument returns undef' );
96
97 my $my_message = {
98     borrowernumber         => $borrowernumber,
99     message_transport_type => 'sms',
100     to_address             => undef,
101     from_address           => 'from@example.com',
102 };
103 my $message_id = C4::Letters::EnqueueLetter($my_message);
104 is( $message_id, undef, 'EnqueueLetter without the letter argument returns undef' );
105
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',
113 };
114
115 $message_id = C4::Letters::EnqueueLetter($my_message);
116 is( $message_id, undef, 'EnqueueLetter without the message type argument argument returns undef' );
117
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');
121
122
123 # GetQueuedMessages
124 my $messages = C4::Letters::GetQueuedMessages();
125 is( @$messages, 1, 'GetQueuedMessages without argument returns all the entries' );
126
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' );
135
136
137 # SendQueuedMessages
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 });
143 is(
144     $messages->[0]->{status},
145     'failed',
146     'message marked failed if tried to send SMS message for borrower with no smsalertnumber set (bug 11208)'
147 );
148
149 # ResendMessage
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' );
158
159 # GetLetters
160 my $letters = C4::Letters::GetLetters();
161 is( @$letters, 0, 'GetLetters returns the correct number of letters' );
162
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.
166
167 <<branches.branchname>>
168 <<branches.branchaddress1>>
169 URL: <<OPACBaseURL>>
170
171 The following item(s) is/are currently <<status>>:
172
173 <item> <<count>>. <<items.itemcallnumber>>, Barcode: <<items.barcode>> </item>
174
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>>.
178 };
179
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' );
186
187
188 # getletter
189 subtest 'getletter' => sub {
190     plan tests => 16;
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' );
201
202     t::lib::Mocks::mock_userenv({ branchcode => "anotherlib", flags => 1 });
203
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' );
214 };
215
216
217
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'  );
224
225 # test for overdue_notices.pl
226 my $overdue_rules = {
227     letter1         => 'my code',
228 };
229 my $i = 1;
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' );
233
234 # GetPreparedLetter
235 t::lib::Mocks::mock_preference('OPACBaseURL', 'http://thisisatest.com');
236
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 );
239
240 my $tables = {
241     borrowers => $borrowernumber,
242     branches => $library->{branchcode},
243     biblio => $biblionumber,
244 };
245 my $substitute = {
246     status => 'overdue',
247 };
248 my $repeat = [
249     {
250         itemcallnumber => 'my callnumber1',
251         barcode        => '1234',
252     },
253     {
254         itemcallnumber => 'my callnumber2',
255         barcode        => '5678',
256     },
257 ];
258 my $prepared_letter = GetPreparedLetter((
259     module      => 'my module',
260     branchcode  => $library->{branchcode},
261     letter_code => 'my code',
262     tables      => $tables,
263     substitute  => $substitute,
264     repeat      => $repeat,
265 ));
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.
271
272 |.$retrieved_library->branchname.qq|
273 |.$retrieved_library->branchaddress1.qq|
274 URL: http://thisisatest.com
275
276 The following item(s) is/are currently $substitute->{status}:
277
278 <item> 1. $repeat->[0]->{itemcallnumber}, Barcode: $repeat->[0]->{barcode} </item>
279 <item> 2. $repeat->[1]->{itemcallnumber}, Barcode: $repeat->[1]->{barcode} </item>
280
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";
284
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' );
287
288 $prepared_letter = GetPreparedLetter((
289     module                 => 'my module',
290     branchcode             => $library->{branchcode},
291     letter_code            => 'my code',
292     tables                 => $tables,
293     substitute             => $substitute,
294     repeat                 => $repeat,
295     message_transport_type => 'sms',
296 ));
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' );
299
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',
303     branchcode             => '',
304     letter_code            => 'test_date',
305     tables                 => $tables,
306     substitute             => $substitute,
307     repeat                 => $repeat,
308 ));
309 is( $prepared_letter->{content}, q|This one only contains the date: | . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 1' );
310
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',
314     branchcode             => '',
315     letter_code            => 'test_date',
316     tables                 => $tables,
317     substitute             => $substitute,
318     repeat                 => $repeat,
319 ));
320 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 2' );
321
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',
325     branchcode             => '',
326     letter_code            => 'test_date',
327     tables                 => $tables,
328     substitute             => $substitute,
329     repeat                 => $repeat,
330 ));
331 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 3' );
332
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',
339     branchcode             => '',
340     letter_code            => 'test_date',
341     tables                 => $tables,
342     substitute             => $substitute,
343     repeat                 => $repeat,
344 ));
345 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $yesterday_night }) . q|.|, 'dateonly test 3' );
346
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>');});
349
350 # Test that _parseletter doesn't modify its parameters bug 15429
351 {
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" );
355 }
356
357 # Correctly format dateexpiry
358 {
359     my $values = { dateexpiry => '2015-12-13', };
360
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' );
365
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' );
370 }
371
372 my $bookseller = Koha::Acquisition::Bookseller->new(
373     {
374         name => "my vendor",
375         address1 => "bookseller's address",
376         phone => "0123456",
377         active => 1,
378         deliverytime => 5,
379     }
380 )->store;
381 my $booksellerid = $bookseller->id;
382
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);
386
387 my $budgetid = C4::Budgets::AddBudget({
388     budget_code => "budget_code_test_letters",
389     budget_name => "budget_name_test_letters",
390 });
391
392 my $bib = MARC::Record->new();
393 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
394     $bib->append_fields(
395         MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
396     );
397 } else {
398     $bib->append_fields(
399         MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
400     );
401 }
402
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 });
405
406 ($biblionumber, $biblioitemnumber) = AddBiblio($bib, '');
407 my $order = Koha::Acquisition::Order->new(
408     {
409         basketno => $basketno,
410         quantity => 1,
411         biblionumber => $biblionumber,
412         budget_id => $budgetid,
413     }
414 )->store;
415 my $ordernumber = $order->ordernumber;
416
417 C4::Acquisition::CloseBasket( $basketno );
418 my $err;
419 warning_like {
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");
424
425 $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
426 $bookseller->contacts->next->email('testemail@mydomain.com')->store;
427
428 # Ensure that the preference 'LetterLog' is set to logging
429 t::lib::Mocks::mock_preference( 'LetterLog', 'on' );
430
431 # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
432 t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
433
434 {
435 warning_is {
436     $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ) }
437     "Fake sendmail",
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');
442
443 $dbh->do(q{DELETE FROM letter WHERE code = 'TESTACQORDER';});
444 warning_like {
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.");
449 }
450
451 {
452 warning_is {
453     $err = SendAlerts( 'claimacquisition', [ $ordernumber ], 'TESTACQCLAIM' ) }
454     "Fake sendmail",
455     "SendAlerts is using the mocked sendmail routine";
456
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');
460 }
461
462 {
463 use C4::Serials;
464
465 my $notes = 'notes';
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
475 );
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];
479
480 my $patron = Koha::Patron->new({
481     firstname    => 'John',
482     surname      => 'Smith',
483     categorycode => $patron_category,
484     branchcode   => $library->{branchcode},
485     dateofbirth  => $date,
486     email        => 'john.smith@test.de',
487 })->store;
488 my $borrowernumber = $patron->borrowernumber;
489 my $subscription = Koha::Subscriptions->find( $subscriptionid );
490 $subscription->add_subscriber( $patron );
491
492 my $err2;
493 warning_is {
494 $err2 = SendAlerts( 'issue', $serial->{serialid}, 'RLIST' ) }
495     "Fake sendmail",
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');
500 }
501
502 subtest 'SendAlerts - claimissue' => sub {
503     plan tests => 8;
504
505     use C4::Serials;
506
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>>');});
508
509     my $bookseller = Koha::Acquisition::Bookseller->new(
510         {
511             name => "my vendor",
512             address1 => "bookseller's address",
513             phone => "0123456",
514             active => 1,
515             deliverytime => 5,
516         }
517     )->store;
518     my $booksellerid = $bookseller->id;
519
520     Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues => 1, booksellerid => $booksellerid } )->store;
521
522     my $bib = MARC::Record->new();
523     if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
524         $bib->append_fields(
525             MARC::Field->new('011', ' ', ' ', a => 'xxxx-yyyy'),
526             MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
527         );
528     } else {
529         $bib->append_fields(
530             MARC::Field->new('022', ' ', ' ', a => 'xxxx-yyyy'),
531             MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
532         );
533     }
534     my ($biblionumber) = AddBiblio($bib, '');
535
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
544     );
545
546     my ($serials_count, @serials) = GetSerials($subscriptionid);
547     my  @serialids = ($serials[0]->{serialid});
548
549     my $err;
550     warning_like {
551         $err = SendAlerts( 'claimissues', \@serialids, 'TESTSERIALCLAIM' ) }
552         qr/^Bookseller .* without emails at/,
553         "Warn on vendor without email address";
554
555     $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
556     $bookseller->contacts->next->email('testemail@mydomain.com')->store;
557
558     # Ensure that the preference 'LetterLog' is set to logging
559     t::lib::Mocks::mock_preference( 'LetterLog', 'on' );
560
561     # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
562     t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
563
564     t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
565
566     {
567     warning_is {
568         $err = SendAlerts( 'claimissues', \@serialids , 'TESTSERIALCLAIM' ) }
569         "Fake sendmail",
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');
574     }
575
576     {
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});
582
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");
585
586     $dbh->do(q{DELETE FROM letter WHERE code = 'TESTSERIALCLAIM';});
587     warning_like {
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");
592     }
593
594 };
595
596 subtest 'GetPreparedLetter' => sub {
597     plan tests => 4;
598
599     Koha::Notice::Template->new(
600         {
601             module                 => 'test',
602             code                   => 'test',
603             branchcode             => '',
604             message_transport_type => 'email'
605         }
606     )->store;
607     my $letter;
608     warning_like {
609         $letter = C4::Letters::GetPreparedLetter(
610             module      => 'test',
611             letter_code => 'test',
612         );
613     }
614     qr{^ERROR: nothing to substitute},
615 'GetPreparedLetter should warn if tables, substiture and repeat are not set';
616     is( $letter, undef,
617 'No letter should be returned by GetPreparedLetter if something went wrong'
618     );
619
620     warning_like {
621         $letter = C4::Letters::GetPreparedLetter(
622             module      => 'test',
623             letter_code => 'test',
624             substitute  => {}
625         );
626     }
627     qr{^ERROR: nothing to substitute},
628 'GetPreparedLetter should warn if tables, substiture and repeat are not set, even if the key is passed';
629     is( $letter, undef,
630 'No letter should be returned by GetPreparedLetter if something went wrong'
631     );
632
633 };
634
635
636
637 subtest 'TranslateNotices' => sub {
638     plan tests => 4;
639
640     t::lib::Mocks::mock_preference( 'TranslateNotices', '1' );
641
642     $dbh->do(
643         q|
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');
647     | );
648     my $substitute = {};
649     my $letter = C4::Letters::GetPreparedLetter(
650             module                 => 'test',
651             tables                 => $tables,
652             letter_code            => 'code',
653             message_transport_type => 'email',
654             substitute             => $substitute,
655     );
656     is(
657         $letter->{title},
658         'a test',
659         'GetPreparedLetter should return the default one if the lang parameter is not provided'
660     );
661
662     $letter = C4::Letters::GetPreparedLetter(
663             module                 => 'test',
664             tables                 => $tables,
665             letter_code            => 'code',
666             message_transport_type => 'email',
667             substitute             => $substitute,
668             lang                   => 'es-ES',
669     );
670     is( $letter->{title}, 'una prueba',
671         'GetPreparedLetter should return the required notice if it exists' );
672
673     $letter = C4::Letters::GetPreparedLetter(
674             module                 => 'test',
675             tables                 => $tables,
676             letter_code            => 'code',
677             message_transport_type => 'email',
678             substitute             => $substitute,
679             lang                   => 'fr-FR',
680     );
681     is(
682         $letter->{title},
683         'a test',
684         'GetPreparedLetter should return the default notice if the one required does not exist'
685     );
686
687     t::lib::Mocks::mock_preference( 'TranslateNotices', '' );
688
689     $letter = C4::Letters::GetPreparedLetter(
690             module                 => 'test',
691             tables                 => $tables,
692             letter_code            => 'code',
693             message_transport_type => 'email',
694             substitute             => $substitute,
695             lang                   => 'es-ES',
696     );
697     is( $letter->{title}, 'a test',
698         'GetPreparedLetter should return the default notice if pref disabled but additional language exists' );
699
700 };
701
702 subtest 'SendQueuedMessages' => sub {
703
704     plan tests => 6;
705
706     t::lib::Mocks::mock_preference( 'SMSSendDriver', 'Email' );
707     t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', '');
708
709     my $patron = Koha::Patrons->find($borrowernumber);
710     $dbh->do(q|
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
714     );
715     eval { C4::Letters::SendQueuedMessages(); };
716     is( $@, '', 'SendQueuedMessages should not explode if the patron does not have a sms provider set' );
717
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();
722
723     my $message = $schema->resultset('MessageQueue')->search({
724         borrowernumber => $borrowernumber,
725         status => 'sent'
726     })->next();
727
728     is( $message->to_address(), '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address not set' );
729     is(
730         $message->from_address(),
731         'from@example.com',
732         'SendQueuedMessages uses message queue item \"from address\" for SMS by email when EmailSMSSendDriverFromAddress system preference is not set'
733     );
734
735     $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber, status => 'sent'})->delete(); #clear borrower queue
736
737     t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', 'override@example.com');
738
739     $message_id = C4::Letters::EnqueueLetter($my_message);
740     C4::Letters::SendQueuedMessages();
741
742     $message = $schema->resultset('MessageQueue')->search({
743         borrowernumber => $borrowernumber,
744         status => 'sent'
745     })->next();
746
747     is(
748         $message->from_address(),
749         'override@example.com',
750         'SendQueuedMessages uses EmailSMSSendDriverFromAddress value for SMS by email when EmailSMSSendDriverFromAddress is set'
751     );
752
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);
756
757     my $number_attempted = C4::Letters::SendQueuedMessages({
758         borrowernumber => -1, # -1 still triggers the borrowernumber condition
759         letter_code    => 'PASSWORD_RESET',
760     });
761     is ( $number_attempted, 0, 'There were no password reset messages for SendQueuedMessages to attempt.' );
762
763     C4::Letters::SendQueuedMessages();
764     my $sms_message_address = $schema->resultset('MessageQueue')->search({
765         borrowernumber => $borrowernumber,
766         status => 'sent'
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' );
769
770 };
771
772 subtest 'get_item_content' => sub {
773     plan tests => 2;
774
775     t::lib::Mocks::mock_preference('dateformat', 'metric');
776     t::lib::Mocks::mock_preference('timeformat', '24hr');
777     my @items = (
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 },
780     );
781     my @item_content_fields = qw( date_due title barcode author itemnumber );
782
783     my $items_content;
784     for my $item ( @items ) {
785         $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields } );
786     }
787
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
791 EOF
792     is( $items_content, $expected_items_content, 'get_item_content should return correct items info with time (default)' );
793
794
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, } );
798     }
799
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
803 EOF
804     is( $items_content, $expected_items_content, 'get_item_content should return correct items info without time (if dateonly => 1)' );
805 };
806
807 subtest 'Test limit parameter for SendQueuedMessages' => sub {
808     plan tests => 3;
809
810     my $dbh = C4::Context->dbh;
811
812     my $borrowernumber = Koha::Patron->new({
813         firstname    => 'Jane',
814         surname      => 'Smith',
815         categorycode => $patron_category,
816         branchcode   => $library->{branchcode},
817         dateofbirth  => $date,
818         smsalertnumber => undef,
819     })->store->borrowernumber;
820
821     $dbh->do(q|DELETE FROM message_queue|);
822     $my_message = {
823         'letter' => {
824             'content'      => 'a message',
825             'metadata'     => 'metadata',
826             'code'         => 'TEST_MESSAGE',
827             'content_type' => 'text/plain',
828             'title'        => 'message title'
829         },
830         'borrowernumber'         => $borrowernumber,
831         'to_address'             => undef,
832         'message_transport_type' => 'sms',
833         'from_address'           => 'from@example.com'
834     };
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' );
849 };