Bug 22808: Move Cache.t to db_dependent
[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 => 69;
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 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', '' );
237
238 my $sms_content = 'This is a SMS for an <<status>>';
239 $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 );
240
241 my $tables = {
242     borrowers => $borrowernumber,
243     branches => $library->{branchcode},
244     biblio => $biblionumber,
245 };
246 my $substitute = {
247     status => 'overdue',
248 };
249 my $repeat = [
250     {
251         itemcallnumber => 'my callnumber1',
252         barcode        => '1234',
253     },
254     {
255         itemcallnumber => 'my callnumber2',
256         barcode        => '5678',
257     },
258 ];
259 my $prepared_letter = GetPreparedLetter((
260     module      => 'my module',
261     branchcode  => $library->{branchcode},
262     letter_code => 'my code',
263     tables      => $tables,
264     substitute  => $substitute,
265     repeat      => $repeat,
266 ));
267 my $retrieved_library = Koha::Libraries->find($library->{branchcode});
268 my $my_title_letter = $retrieved_library->branchname . qq| - $substitute->{status}|;
269 my $biblio_timestamp = dt_from_string( GetBiblioData($biblionumber)->{timestamp} );
270 my $my_content_letter = qq|Dear Jane Smith,
271 According to our current records, you have items that are overdue.Your library does not charge late fines, but please return or renew them at the branch below as soon as possible.
272
273 |.$retrieved_library->branchname.qq|
274 |.$retrieved_library->branchaddress1.qq|
275 URL: http://thisisatest.com
276
277 The following item(s) is/are currently $substitute->{status}:
278
279 <item> 1. $repeat->[0]->{itemcallnumber}, Barcode: $repeat->[0]->{barcode} </item>
280 <item> 2. $repeat->[1]->{itemcallnumber}, Barcode: $repeat->[1]->{barcode} </item>
281
282 Thank-you for your prompt attention to this matter.
283 Don't forget your date of birth: | . output_pref({ dt => $date, dateonly => 1 }) . q|.
284 Look at this wonderful biblio timestamp: | . output_pref({ dt => $biblio_timestamp })  . ".\n";
285
286 is( $prepared_letter->{title}, $my_title_letter, 'GetPreparedLetter returns the title correctly' );
287 is( $prepared_letter->{content}, $my_content_letter, 'GetPreparedLetter returns the content correctly' );
288
289 $prepared_letter = GetPreparedLetter((
290     module                 => 'my module',
291     branchcode             => $library->{branchcode},
292     letter_code            => 'my code',
293     tables                 => $tables,
294     substitute             => $substitute,
295     repeat                 => $repeat,
296     message_transport_type => 'sms',
297 ));
298 $my_content_letter = qq|This is a SMS for an $substitute->{status}|;
299 is( $prepared_letter->{content}, $my_content_letter, 'GetPreparedLetter returns the content correctly' );
300
301 $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>>.');});
302 $prepared_letter = GetPreparedLetter((
303     module                 => 'test_date',
304     branchcode             => '',
305     letter_code            => 'test_date',
306     tables                 => $tables,
307     substitute             => $substitute,
308     repeat                 => $repeat,
309 ));
310 is( $prepared_letter->{content}, q|This one only contains the date: | . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 1' );
311
312 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp | dateonly>>.' WHERE code = 'test_date';});
313 $prepared_letter = GetPreparedLetter((
314     module                 => 'test_date',
315     branchcode             => '',
316     letter_code            => 'test_date',
317     tables                 => $tables,
318     substitute             => $substitute,
319     repeat                 => $repeat,
320 ));
321 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 2' );
322
323 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp|dateonly >>.' WHERE code = 'test_date';});
324 $prepared_letter = GetPreparedLetter((
325     module                 => 'test_date',
326     branchcode             => '',
327     letter_code            => 'test_date',
328     tables                 => $tables,
329     substitute             => $substitute,
330     repeat                 => $repeat,
331 ));
332 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $date, dateonly => 1 }) . q|.|, 'dateonly test 3' );
333
334 t::lib::Mocks::mock_preference( 'TimeFormat', '12hr' );
335 my $yesterday_night = $date->clone->add( days => -1 )->set_hour(22);
336 $dbh->do(q|UPDATE biblio SET timestamp = ? WHERE biblionumber = ?|, undef, $yesterday_night, $biblionumber );
337 $dbh->do(q{UPDATE letter SET content = 'And also this one:<<timestamp>>.' WHERE code = 'test_date';});
338 $prepared_letter = GetPreparedLetter((
339     module                 => 'test_date',
340     branchcode             => '',
341     letter_code            => 'test_date',
342     tables                 => $tables,
343     substitute             => $substitute,
344     repeat                 => $repeat,
345 ));
346 is( $prepared_letter->{content}, q|And also this one:| . output_pref({ dt => $yesterday_night }) . q|.|, 'dateonly test 3' );
347
348 $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>');});
349 $dbh->do(q{INSERT INTO letter (module, code, name, title, content) VALUES ('orderacquisition','TESTACQORDER','Acquisition Order','Order','<<aqbooksellers.name>>|<<aqcontacts.name>>|<order>Ordernumber <<aqorders.ordernumber>> (<<biblio.title>>) (<<aqorders.quantity>> ordered)</order>');});
350
351 # Test that _parseletter doesn't modify its parameters bug 15429
352 {
353     my $values = { dateexpiry => '2015-12-13', };
354     C4::Letters::_parseletter($prepared_letter, 'borrowers', $values);
355     is( $values->{dateexpiry}, '2015-12-13', "_parseletter doesn't modify its parameters" );
356 }
357
358 # Correctly format dateexpiry
359 {
360     my $values = { dateexpiry => '2015-12-13', };
361
362     t::lib::Mocks::mock_preference('dateformat', 'metric');
363     t::lib::Mocks::mock_preference('timeformat', '24hr');
364     my $letter = C4::Letters::_parseletter({ content => "expiry on <<borrowers.dateexpiry>>"}, 'borrowers', $values);
365     is( $letter->{content}, 'expiry on 13/12/2015' );
366
367     t::lib::Mocks::mock_preference('dateformat', 'metric');
368     t::lib::Mocks::mock_preference('timeformat', '12hr');
369     $letter = C4::Letters::_parseletter({ content => "expiry on <<borrowers.dateexpiry>>"}, 'borrowers', $values);
370     is( $letter->{content}, 'expiry on 13/12/2015' );
371 }
372
373 my $bookseller = Koha::Acquisition::Bookseller->new(
374     {
375         name => "my vendor",
376         address1 => "bookseller's address",
377         phone => "0123456",
378         active => 1,
379         deliverytime => 5,
380     }
381 )->store;
382 my $booksellerid = $bookseller->id;
383
384 Koha::Acquisition::Bookseller::Contact->new( { name => 'John Smith',  phone => '0123456x1', claimacquisition => 1, orderacquisition => 1, booksellerid => $booksellerid } )->store;
385 Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues      => 1, booksellerid => $booksellerid } )->store;
386 my $basketno = NewBasket($booksellerid, 1);
387
388 my $budgetid = C4::Budgets::AddBudget({
389     budget_code => "budget_code_test_letters",
390     budget_name => "budget_name_test_letters",
391 });
392
393 my $bib = MARC::Record->new();
394 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
395     $bib->append_fields(
396         MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
397     );
398 } else {
399     $bib->append_fields(
400         MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
401     );
402 }
403
404 my $logged_in_user = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $library->{branchcode} }});
405 t::lib::Mocks::mock_userenv({ patron => $logged_in_user });
406
407 ($biblionumber, $biblioitemnumber) = AddBiblio($bib, '');
408 my $order = Koha::Acquisition::Order->new(
409     {
410         basketno => $basketno,
411         quantity => 1,
412         biblionumber => $biblionumber,
413         budget_id => $budgetid,
414     }
415 )->store;
416 my $ordernumber = $order->ordernumber;
417
418 C4::Acquisition::CloseBasket( $basketno );
419 my $err;
420 warning_like {
421     $err = SendAlerts( 'claimacquisition', [ $ordernumber ], 'TESTACQCLAIM' ) }
422     qr/^Bookseller .* without emails at/,
423     "SendAlerts prints a warning";
424 is($err->{'error'}, 'no_email', "Trying to send an alert when there's no e-mail results in an error");
425
426 $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
427 $bookseller->contacts->next->email('testemail@mydomain.com')->store;
428
429 # Ensure that the preference 'LetterLog' is set to logging
430 t::lib::Mocks::mock_preference( 'LetterLog', 'on' );
431
432 # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
433 t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
434
435 {
436 warning_is {
437     $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ) }
438     "Fake sendmail",
439     "SendAlerts is using the mocked sendmail routine (orderacquisition)";
440 is($err, 1, "Successfully sent order.");
441 is($mail{'To'}, 'testemail@mydomain.com', "mailto correct in sent order");
442 is($mail{'Message'}, 'my vendor|John Smith|Ordernumber ' . $ordernumber . ' (Silence in the library) (1 ordered)', 'Order notice text constructed successfully');
443
444 $dbh->do(q{DELETE FROM letter WHERE code = 'TESTACQORDER';});
445 warning_like {
446     $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ) }
447     qr/No orderacquisition TESTACQORDER letter transported by email/,
448     "GetPreparedLetter warns about missing notice template";
449 is($err->{'error'}, 'no_letter', "No TESTACQORDER letter was defined.");
450 }
451
452 {
453 warning_is {
454     $err = SendAlerts( 'claimacquisition', [ $ordernumber ], 'TESTACQCLAIM' ) }
455     "Fake sendmail",
456     "SendAlerts is using the mocked sendmail routine";
457
458 is($err, 1, "Successfully sent claim");
459 is($mail{'To'}, 'testemail@mydomain.com', "mailto correct in sent claim");
460 is($mail{'Message'}, 'my vendor|John Smith|Ordernumber ' . $ordernumber . ' (Silence in the library) (1 ordered)', 'Claim notice text constructed successfully');
461 }
462
463 {
464 use C4::Serials;
465
466 my $notes = 'notes';
467 my $internalnotes = 'intnotes';
468 $dbh->do(q|UPDATE subscription_numberpatterns SET numberingmethod='No. {X}' WHERE id=1|);
469 my $subscriptionid = NewSubscription(
470      undef,      "",     undef, undef, undef, $biblionumber,
471     '2013-01-01', 1, undef, undef,  undef,
472     undef,      undef,  undef, undef, undef, undef,
473     1,          $notes,undef, '2013-01-01', undef, 1,
474     undef,       undef,  0,    $internalnotes,  0,
475     undef, undef, 0,          undef,         '2013-12-31', 0
476 );
477 $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>>');});
478 my ($serials_count, @serials) = GetSerials($subscriptionid);
479 my $serial = $serials[0];
480
481 my $patron = Koha::Patron->new({
482     firstname    => 'John',
483     surname      => 'Smith',
484     categorycode => $patron_category,
485     branchcode   => $library->{branchcode},
486     dateofbirth  => $date,
487     email        => 'john.smith@test.de',
488 })->store;
489 my $borrowernumber = $patron->borrowernumber;
490 my $subscription = Koha::Subscriptions->find( $subscriptionid );
491 $subscription->add_subscriber( $patron );
492
493 my $err2;
494 warning_is {
495 $err2 = SendAlerts( 'issue', $serial->{serialid}, 'RLIST' ) }
496     "Fake sendmail",
497     "SendAlerts is using the mocked sendmail routine";
498 is($err2, 1, "Successfully sent serial notification");
499 is($mail{'To'}, 'john.smith@test.de', "mailto correct in sent serial notification");
500 is($mail{'Message'}, 'Silence in the library,'.$subscriptionid.',No. 0', 'Serial notification text constructed successfully');
501
502 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', 'robert.tables@mail.com' );
503
504 my $err3;
505 warning_is {
506 $err3 = SendAlerts( 'issue', $serial->{serialid}, 'RLIST' ) }
507     "Fake sendmail",
508     "SendAlerts is using the mocked sendmail routine";
509 is($mail{'To'}, 'robert.tables@mail.com', "mailto address overwritten by SendAllMailsTo preference");
510 }
511 t::lib::Mocks::mock_preference( 'SendAllEmailsTo', '' );
512
513 subtest 'SendAlerts - claimissue' => sub {
514     plan tests => 8;
515
516     use C4::Serials;
517
518     $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>>');});
519
520     my $bookseller = Koha::Acquisition::Bookseller->new(
521         {
522             name => "my vendor",
523             address1 => "bookseller's address",
524             phone => "0123456",
525             active => 1,
526             deliverytime => 5,
527         }
528     )->store;
529     my $booksellerid = $bookseller->id;
530
531     Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues => 1, booksellerid => $booksellerid } )->store;
532
533     my $bib = MARC::Record->new();
534     if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
535         $bib->append_fields(
536             MARC::Field->new('011', ' ', ' ', a => 'xxxx-yyyy'),
537             MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
538         );
539     } else {
540         $bib->append_fields(
541             MARC::Field->new('022', ' ', ' ', a => 'xxxx-yyyy'),
542             MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
543         );
544     }
545     my ($biblionumber) = AddBiblio($bib, '');
546
547     $dbh->do(q|UPDATE subscription_numberpatterns SET numberingmethod='No. {X}' WHERE id=1|);
548     my $subscriptionid = NewSubscription(
549          undef, "", $booksellerid, undef, undef, $biblionumber,
550         '2013-01-01', 1, undef, undef,  undef,
551         undef,  undef,  undef, undef, undef, undef,
552         1, 'public',undef, '2013-01-01', undef, 1,
553         undef, undef,  0, 'internal',  0,
554         undef, undef, 0,  undef, '2013-12-31', 0
555     );
556
557     my ($serials_count, @serials) = GetSerials($subscriptionid);
558     my  @serialids = ($serials[0]->{serialid});
559
560     my $err;
561     warning_like {
562         $err = SendAlerts( 'claimissues', \@serialids, 'TESTSERIALCLAIM' ) }
563         qr/^Bookseller .* without emails at/,
564         "Warn on vendor without email address";
565
566     $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
567     $bookseller->contacts->next->email('testemail@mydomain.com')->store;
568
569     # Ensure that the preference 'LetterLog' is set to logging
570     t::lib::Mocks::mock_preference( 'LetterLog', 'on' );
571
572     # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
573     t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
574
575     t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
576
577     {
578     warning_is {
579         $err = SendAlerts( 'claimissues', \@serialids , 'TESTSERIALCLAIM' ) }
580         "Fake sendmail",
581         "SendAlerts is using the mocked sendmail routine (claimissues)";
582     is($err, 1, "Successfully sent claim");
583     is($mail{'To'}, 'testemail@mydomain.com', "mailto correct in sent claim");
584     is($mail{'Message'}, "$serialids[0]|2013-01-01|Silence in the library|xxxx-yyyy", 'Serial claim letter for 1 issue constructed successfully');
585     }
586
587     {
588     my $publisheddate = output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 });
589     my $serialexpected = ( C4::Serials::findSerialsByStatus( 1, $subscriptionid ) )[0];
590     ModSerialStatus( $serials[0]->{serialid}, "No. 1", $publisheddate, $publisheddate, $publisheddate, '3', 'a note' );
591     ($serials_count, @serials) = GetSerials($subscriptionid);
592     push @serialids, ($serials[1]->{serialid});
593
594     $err = SendAlerts( 'claimissues', \@serialids , 'TESTSERIALCLAIM' );
595     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");
596
597     $dbh->do(q{DELETE FROM letter WHERE code = 'TESTSERIALCLAIM';});
598     warning_like {
599         $err = SendAlerts( 'orderacquisition', $basketno , 'TESTSERIALCLAIM' ) }
600         qr/No orderacquisition TESTSERIALCLAIM letter transported by email/,
601         "GetPreparedLetter warns about missing notice template";
602     is($err->{'error'}, 'no_letter', "No TESTSERIALCLAIM letter was defined");
603     }
604
605 };
606
607 subtest 'GetPreparedLetter' => sub {
608     plan tests => 4;
609
610     Koha::Notice::Template->new(
611         {
612             module                 => 'test',
613             code                   => 'test',
614             branchcode             => '',
615             message_transport_type => 'email'
616         }
617     )->store;
618     my $letter;
619     warning_like {
620         $letter = C4::Letters::GetPreparedLetter(
621             module      => 'test',
622             letter_code => 'test',
623         );
624     }
625     qr{^ERROR: nothing to substitute},
626 'GetPreparedLetter should warn if tables, substiture and repeat are not set';
627     is( $letter, undef,
628 'No letter should be returned by GetPreparedLetter if something went wrong'
629     );
630
631     warning_like {
632         $letter = C4::Letters::GetPreparedLetter(
633             module      => 'test',
634             letter_code => 'test',
635             substitute  => {}
636         );
637     }
638     qr{^ERROR: nothing to substitute},
639 'GetPreparedLetter should warn if tables, substiture and repeat are not set, even if the key is passed';
640     is( $letter, undef,
641 'No letter should be returned by GetPreparedLetter if something went wrong'
642     );
643
644 };
645
646
647
648 subtest 'TranslateNotices' => sub {
649     plan tests => 4;
650
651     t::lib::Mocks::mock_preference( 'TranslateNotices', '1' );
652
653     $dbh->do(
654         q|
655         INSERT INTO letter (module, code, branchcode, name, title, content, message_transport_type, lang) VALUES
656         ('test', 'code', '', 'test', 'a test', 'just a test', 'email', 'default'),
657         ('test', 'code', '', 'test', 'una prueba', 'solo una prueba', 'email', 'es-ES');
658     | );
659     my $substitute = {};
660     my $letter = C4::Letters::GetPreparedLetter(
661             module                 => 'test',
662             tables                 => $tables,
663             letter_code            => 'code',
664             message_transport_type => 'email',
665             substitute             => $substitute,
666     );
667     is(
668         $letter->{title},
669         'a test',
670         'GetPreparedLetter should return the default one if the lang parameter is not provided'
671     );
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                   => 'es-ES',
680     );
681     is( $letter->{title}, 'una prueba',
682         'GetPreparedLetter should return the required notice if it exists' );
683
684     $letter = C4::Letters::GetPreparedLetter(
685             module                 => 'test',
686             tables                 => $tables,
687             letter_code            => 'code',
688             message_transport_type => 'email',
689             substitute             => $substitute,
690             lang                   => 'fr-FR',
691     );
692     is(
693         $letter->{title},
694         'a test',
695         'GetPreparedLetter should return the default notice if the one required does not exist'
696     );
697
698     t::lib::Mocks::mock_preference( 'TranslateNotices', '' );
699
700     $letter = C4::Letters::GetPreparedLetter(
701             module                 => 'test',
702             tables                 => $tables,
703             letter_code            => 'code',
704             message_transport_type => 'email',
705             substitute             => $substitute,
706             lang                   => 'es-ES',
707     );
708     is( $letter->{title}, 'a test',
709         'GetPreparedLetter should return the default notice if pref disabled but additional language exists' );
710
711 };
712
713 subtest 'SendQueuedMessages' => sub {
714
715     plan tests => 6;
716
717     t::lib::Mocks::mock_preference( 'SMSSendDriver', 'Email' );
718     t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', '');
719
720     my $patron = Koha::Patrons->find($borrowernumber);
721     $dbh->do(q|
722         INSERT INTO message_queue(borrowernumber, subject, content, message_transport_type, status, letter_code)
723         VALUES (?, 'subject', 'content', 'sms', 'pending', 'just_a_code')
724         |, undef, $borrowernumber
725     );
726     eval { C4::Letters::SendQueuedMessages(); };
727     is( $@, '', 'SendQueuedMessages should not explode if the patron does not have a sms provider set' );
728
729     my $sms_pro = $builder->build_object({ class => 'Koha::SMS::Providers', value => { domain => 'kidclamp.rocks' } });
730     $patron->set( { smsalertnumber => '5555555555', sms_provider_id => $sms_pro->id() } )->store;
731     $message_id = C4::Letters::EnqueueLetter($my_message); #using datas set around line 95 and forward
732     C4::Letters::SendQueuedMessages();
733
734     my $message = $schema->resultset('MessageQueue')->search({
735         borrowernumber => $borrowernumber,
736         status => 'sent'
737     })->next();
738
739     is( $message->to_address(), '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address not set' );
740     is(
741         $message->from_address(),
742         'from@example.com',
743         'SendQueuedMessages uses message queue item \"from address\" for SMS by email when EmailSMSSendDriverFromAddress system preference is not set'
744     );
745
746     $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber, status => 'sent'})->delete(); #clear borrower queue
747
748     t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', 'override@example.com');
749
750     $message_id = C4::Letters::EnqueueLetter($my_message);
751     C4::Letters::SendQueuedMessages();
752
753     $message = $schema->resultset('MessageQueue')->search({
754         borrowernumber => $borrowernumber,
755         status => 'sent'
756     })->next();
757
758     is(
759         $message->from_address(),
760         'override@example.com',
761         'SendQueuedMessages uses EmailSMSSendDriverFromAddress value for SMS by email when EmailSMSSendDriverFromAddress is set'
762     );
763
764     $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber,status => 'sent'})->delete(); #clear borrower queue
765     $my_message->{to_address} = 'fixme@kidclamp.iswrong';
766     $message_id = C4::Letters::EnqueueLetter($my_message);
767
768     my $number_attempted = C4::Letters::SendQueuedMessages({
769         borrowernumber => -1, # -1 still triggers the borrowernumber condition
770         letter_code    => 'PASSWORD_RESET',
771     });
772     is ( $number_attempted, 0, 'There were no password reset messages for SendQueuedMessages to attempt.' );
773
774     C4::Letters::SendQueuedMessages();
775     my $sms_message_address = $schema->resultset('MessageQueue')->search({
776         borrowernumber => $borrowernumber,
777         status => 'sent'
778     })->next()->to_address();
779     is( $sms_message_address, '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address is set incorrectly' );
780
781 };
782
783 subtest 'get_item_content' => sub {
784     plan tests => 2;
785
786     t::lib::Mocks::mock_preference('dateformat', 'metric');
787     t::lib::Mocks::mock_preference('timeformat', '24hr');
788     my @items = (
789         {date_due => '2041-01-01 12:34', title => 'a first title', barcode => 'a_first_barcode', author => 'a_first_author', itemnumber => 1 },
790         {date_due => '2042-01-02 23:45', title => 'a second title', barcode => 'a_second_barcode', author => 'a_second_author', itemnumber => 2 },
791     );
792     my @item_content_fields = qw( date_due title barcode author itemnumber );
793
794     my $items_content;
795     for my $item ( @items ) {
796         $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields } );
797     }
798
799     my $expected_items_content = <<EOF;
800 01/01/2041 12:34\ta first title\ta_first_barcode\ta_first_author\t1
801 02/01/2042 23:45\ta second title\ta_second_barcode\ta_second_author\t2
802 EOF
803     is( $items_content, $expected_items_content, 'get_item_content should return correct items info with time (default)' );
804
805
806     $items_content = q||;
807     for my $item ( @items ) {
808         $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields, dateonly => 1, } );
809     }
810
811     $expected_items_content = <<EOF;
812 01/01/2041\ta first title\ta_first_barcode\ta_first_author\t1
813 02/01/2042\ta second title\ta_second_barcode\ta_second_author\t2
814 EOF
815     is( $items_content, $expected_items_content, 'get_item_content should return correct items info without time (if dateonly => 1)' );
816 };
817
818 subtest 'Test limit parameter for SendQueuedMessages' => sub {
819     plan tests => 3;
820
821     my $dbh = C4::Context->dbh;
822
823     my $borrowernumber = Koha::Patron->new({
824         firstname    => 'Jane',
825         surname      => 'Smith',
826         categorycode => $patron_category,
827         branchcode   => $library->{branchcode},
828         dateofbirth  => $date,
829         smsalertnumber => undef,
830     })->store->borrowernumber;
831
832     $dbh->do(q|DELETE FROM message_queue|);
833     $my_message = {
834         'letter' => {
835             'content'      => 'a message',
836             'metadata'     => 'metadata',
837             'code'         => 'TEST_MESSAGE',
838             'content_type' => 'text/plain',
839             'title'        => 'message title'
840         },
841         'borrowernumber'         => $borrowernumber,
842         'to_address'             => undef,
843         'message_transport_type' => 'sms',
844         'from_address'           => 'from@example.com'
845     };
846     C4::Letters::EnqueueLetter($my_message);
847     C4::Letters::EnqueueLetter($my_message);
848     C4::Letters::EnqueueLetter($my_message);
849     C4::Letters::EnqueueLetter($my_message);
850     C4::Letters::EnqueueLetter($my_message);
851     my $messages_processed = C4::Letters::SendQueuedMessages( { limit => 1 } );
852     is( $messages_processed, 1,
853         'Processed 1 message with limit of 1 and 5 unprocessed messages' );
854     $messages_processed = C4::Letters::SendQueuedMessages( { limit => 2 } );
855     is( $messages_processed, 2,
856         'Processed 2 message with limit of 2 and 4 unprocessed messages' );
857     $messages_processed = C4::Letters::SendQueuedMessages( { limit => 3 } );
858     is( $messages_processed, 2,
859         'Processed 2 message with limit of 3 and 2 unprocessed messages' );
860 };