Bug 21992: Remove unused Koha::Patron->update_password method
[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 => 65;
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 my $bookseller = Koha::Acquisition::Bookseller->new(
358     {
359         name => "my vendor",
360         address1 => "bookseller's address",
361         phone => "0123456",
362         active => 1,
363         deliverytime => 5,
364     }
365 )->store;
366 my $booksellerid = $bookseller->id;
367
368 Koha::Acquisition::Bookseller::Contact->new( { name => 'John Smith',  phone => '0123456x1', claimacquisition => 1, orderacquisition => 1, booksellerid => $booksellerid } )->store;
369 Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues      => 1, booksellerid => $booksellerid } )->store;
370 my $basketno = NewBasket($booksellerid, 1);
371
372 my $budgetid = C4::Budgets::AddBudget({
373     budget_code => "budget_code_test_letters",
374     budget_name => "budget_name_test_letters",
375 });
376
377 my $bib = MARC::Record->new();
378 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
379     $bib->append_fields(
380         MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
381     );
382 } else {
383     $bib->append_fields(
384         MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
385     );
386 }
387
388 my $logged_in_user = $builder->build_object({ class => 'Koha::Patrons', value => { branchcode => $library->{branchcode} }});
389 t::lib::Mocks::mock_userenv({ patron => $logged_in_user });
390
391 ($biblionumber, $biblioitemnumber) = AddBiblio($bib, '');
392 my $order = Koha::Acquisition::Order->new(
393     {
394         basketno => $basketno,
395         quantity => 1,
396         biblionumber => $biblionumber,
397         budget_id => $budgetid,
398     }
399 )->store;
400 my $ordernumber = $order->ordernumber;
401
402 C4::Acquisition::CloseBasket( $basketno );
403 my $err;
404 warning_like {
405     $err = SendAlerts( 'claimacquisition', [ $ordernumber ], 'TESTACQCLAIM' ) }
406     qr/^Bookseller .* without emails at/,
407     "SendAlerts prints a warning";
408 is($err->{'error'}, 'no_email', "Trying to send an alert when there's no e-mail results in an error");
409
410 $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
411 $bookseller->contacts->next->email('testemail@mydomain.com')->store;
412
413 # Ensure that the preference 'LetterLog' is set to logging
414 t::lib::Mocks::mock_preference( 'LetterLog', 'on' );
415
416 # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
417 t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
418
419 {
420 warning_is {
421     $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ) }
422     "Fake sendmail",
423     "SendAlerts is using the mocked sendmail routine (orderacquisition)";
424 is($err, 1, "Successfully sent order.");
425 is($mail{'To'}, 'testemail@mydomain.com', "mailto correct in sent order");
426 is($mail{'Message'}, 'my vendor|John Smith|Ordernumber ' . $ordernumber . ' (Silence in the library) (1 ordered)', 'Order notice text constructed successfully');
427
428 $dbh->do(q{DELETE FROM letter WHERE code = 'TESTACQORDER';});
429 warning_like {
430     $err = SendAlerts( 'orderacquisition', $basketno , 'TESTACQORDER' ) }
431     qr/No orderacquisition TESTACQORDER letter transported by email/,
432     "GetPreparedLetter warns about missing notice template";
433 is($err->{'error'}, 'no_letter', "No TESTACQORDER letter was defined.");
434 }
435
436 {
437 warning_is {
438     $err = SendAlerts( 'claimacquisition', [ $ordernumber ], 'TESTACQCLAIM' ) }
439     "Fake sendmail",
440     "SendAlerts is using the mocked sendmail routine";
441
442 is($err, 1, "Successfully sent claim");
443 is($mail{'To'}, 'testemail@mydomain.com', "mailto correct in sent claim");
444 is($mail{'Message'}, 'my vendor|John Smith|Ordernumber ' . $ordernumber . ' (Silence in the library) (1 ordered)', 'Claim notice text constructed successfully');
445 }
446
447 {
448 use C4::Serials;
449
450 my $notes = 'notes';
451 my $internalnotes = 'intnotes';
452 $dbh->do(q|UPDATE subscription_numberpatterns SET numberingmethod='No. {X}' WHERE id=1|);
453 my $subscriptionid = NewSubscription(
454      undef,      "",     undef, undef, undef, $biblionumber,
455     '2013-01-01', 1, undef, undef,  undef,
456     undef,      undef,  undef, undef, undef, undef,
457     1,          $notes,undef, '2013-01-01', undef, 1,
458     undef,       undef,  0,    $internalnotes,  0,
459     undef, undef, 0,          undef,         '2013-12-31', 0
460 );
461 $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>>');});
462 my ($serials_count, @serials) = GetSerials($subscriptionid);
463 my $serial = $serials[0];
464
465 my $patron = Koha::Patron->new({
466     firstname    => 'John',
467     surname      => 'Smith',
468     categorycode => $patron_category,
469     branchcode   => $library->{branchcode},
470     dateofbirth  => $date,
471     email        => 'john.smith@test.de',
472 })->store;
473 my $borrowernumber = $patron->borrowernumber;
474 my $subscription = Koha::Subscriptions->find( $subscriptionid );
475 $subscription->add_subscriber( $patron );
476
477 my $err2;
478 warning_is {
479 $err2 = SendAlerts( 'issue', $serial->{serialid}, 'RLIST' ) }
480     "Fake sendmail",
481     "SendAlerts is using the mocked sendmail routine";
482 is($err2, 1, "Successfully sent serial notification");
483 is($mail{'To'}, 'john.smith@test.de', "mailto correct in sent serial notification");
484 is($mail{'Message'}, 'Silence in the library,'.$subscriptionid.',No. 0', 'Serial notification text constructed successfully');
485 }
486
487 subtest 'SendAlerts - claimissue' => sub {
488     plan tests => 8;
489
490     use C4::Serials;
491
492     $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>>');});
493
494     my $bookseller = Koha::Acquisition::Bookseller->new(
495         {
496             name => "my vendor",
497             address1 => "bookseller's address",
498             phone => "0123456",
499             active => 1,
500             deliverytime => 5,
501         }
502     )->store;
503     my $booksellerid = $bookseller->id;
504
505     Koha::Acquisition::Bookseller::Contact->new( { name => 'Leo Tolstoy', phone => '0123456x2', claimissues => 1, booksellerid => $booksellerid } )->store;
506
507     my $bib = MARC::Record->new();
508     if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
509         $bib->append_fields(
510             MARC::Field->new('011', ' ', ' ', a => 'xxxx-yyyy'),
511             MARC::Field->new('200', ' ', ' ', a => 'Silence in the library'),
512         );
513     } else {
514         $bib->append_fields(
515             MARC::Field->new('022', ' ', ' ', a => 'xxxx-yyyy'),
516             MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'),
517         );
518     }
519     my ($biblionumber) = AddBiblio($bib, '');
520
521     $dbh->do(q|UPDATE subscription_numberpatterns SET numberingmethod='No. {X}' WHERE id=1|);
522     my $subscriptionid = NewSubscription(
523          undef, "", $booksellerid, undef, undef, $biblionumber,
524         '2013-01-01', 1, undef, undef,  undef,
525         undef,  undef,  undef, undef, undef, undef,
526         1, 'public',undef, '2013-01-01', undef, 1,
527         undef, undef,  0, 'internal',  0,
528         undef, undef, 0,  undef, '2013-12-31', 0
529     );
530
531     my ($serials_count, @serials) = GetSerials($subscriptionid);
532     my  @serialids = ($serials[0]->{serialid});
533
534     my $err;
535     warning_like {
536         $err = SendAlerts( 'claimissues', \@serialids, 'TESTSERIALCLAIM' ) }
537         qr/^Bookseller .* without emails at/,
538         "Warn on vendor without email address";
539
540     $bookseller = Koha::Acquisition::Booksellers->find( $booksellerid );
541     $bookseller->contacts->next->email('testemail@mydomain.com')->store;
542
543     # Ensure that the preference 'LetterLog' is set to logging
544     t::lib::Mocks::mock_preference( 'LetterLog', 'on' );
545
546     # SendAlerts needs branchemail or KohaAdminEmailAddress as sender
547     t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
548
549     t::lib::Mocks::mock_preference( 'KohaAdminEmailAddress', 'library@domain.com' );
550
551     {
552     warning_is {
553         $err = SendAlerts( 'claimissues', \@serialids , 'TESTSERIALCLAIM' ) }
554         "Fake sendmail",
555         "SendAlerts is using the mocked sendmail routine (claimissues)";
556     is($err, 1, "Successfully sent claim");
557     is($mail{'To'}, 'testemail@mydomain.com', "mailto correct in sent claim");
558     is($mail{'Message'}, "$serialids[0]|2013-01-01|Silence in the library|xxxx-yyyy", 'Serial claim letter for 1 issue constructed successfully');
559     }
560
561     {
562     my $publisheddate = output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 });
563     my $serialexpected = ( C4::Serials::findSerialsByStatus( 1, $subscriptionid ) )[0];
564     ModSerialStatus( $serials[0]->{serialid}, "No. 1", $publisheddate, $publisheddate, $publisheddate, '3', 'a note' );
565     ($serials_count, @serials) = GetSerials($subscriptionid);
566     push @serialids, ($serials[1]->{serialid});
567
568     $err = SendAlerts( 'claimissues', \@serialids , 'TESTSERIALCLAIM' );
569     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");
570
571     $dbh->do(q{DELETE FROM letter WHERE code = 'TESTSERIALCLAIM';});
572     warning_like {
573         $err = SendAlerts( 'orderacquisition', $basketno , 'TESTSERIALCLAIM' ) }
574         qr/No orderacquisition TESTSERIALCLAIM letter transported by email/,
575         "GetPreparedLetter warns about missing notice template";
576     is($err->{'error'}, 'no_letter', "No TESTSERIALCLAIM letter was defined");
577     }
578
579 };
580
581 subtest 'GetPreparedLetter' => sub {
582     plan tests => 4;
583
584     Koha::Notice::Template->new(
585         {
586             module                 => 'test',
587             code                   => 'test',
588             branchcode             => '',
589             message_transport_type => 'email'
590         }
591     )->store;
592     my $letter;
593     warning_like {
594         $letter = C4::Letters::GetPreparedLetter(
595             module      => 'test',
596             letter_code => 'test',
597         );
598     }
599     qr{^ERROR: nothing to substitute},
600 'GetPreparedLetter should warn if tables, substiture and repeat are not set';
601     is( $letter, undef,
602 'No letter should be returned by GetPreparedLetter if something went wrong'
603     );
604
605     warning_like {
606         $letter = C4::Letters::GetPreparedLetter(
607             module      => 'test',
608             letter_code => 'test',
609             substitute  => {}
610         );
611     }
612     qr{^ERROR: nothing to substitute},
613 'GetPreparedLetter should warn if tables, substiture and repeat are not set, even if the key is passed';
614     is( $letter, undef,
615 'No letter should be returned by GetPreparedLetter if something went wrong'
616     );
617
618 };
619
620
621
622 subtest 'TranslateNotices' => sub {
623     plan tests => 4;
624
625     t::lib::Mocks::mock_preference( 'TranslateNotices', '1' );
626
627     $dbh->do(
628         q|
629         INSERT INTO letter (module, code, branchcode, name, title, content, message_transport_type, lang) VALUES
630         ('test', 'code', '', 'test', 'a test', 'just a test', 'email', 'default'),
631         ('test', 'code', '', 'test', 'una prueba', 'solo una prueba', 'email', 'es-ES');
632     | );
633     my $substitute = {};
634     my $letter = C4::Letters::GetPreparedLetter(
635             module                 => 'test',
636             tables                 => $tables,
637             letter_code            => 'code',
638             message_transport_type => 'email',
639             substitute             => $substitute,
640     );
641     is(
642         $letter->{title},
643         'a test',
644         'GetPreparedLetter should return the default one if the lang parameter is not provided'
645     );
646
647     $letter = C4::Letters::GetPreparedLetter(
648             module                 => 'test',
649             tables                 => $tables,
650             letter_code            => 'code',
651             message_transport_type => 'email',
652             substitute             => $substitute,
653             lang                   => 'es-ES',
654     );
655     is( $letter->{title}, 'una prueba',
656         'GetPreparedLetter should return the required notice if it exists' );
657
658     $letter = C4::Letters::GetPreparedLetter(
659             module                 => 'test',
660             tables                 => $tables,
661             letter_code            => 'code',
662             message_transport_type => 'email',
663             substitute             => $substitute,
664             lang                   => 'fr-FR',
665     );
666     is(
667         $letter->{title},
668         'a test',
669         'GetPreparedLetter should return the default notice if the one required does not exist'
670     );
671
672     t::lib::Mocks::mock_preference( 'TranslateNotices', '' );
673
674     $letter = C4::Letters::GetPreparedLetter(
675             module                 => 'test',
676             tables                 => $tables,
677             letter_code            => 'code',
678             message_transport_type => 'email',
679             substitute             => $substitute,
680             lang                   => 'es-ES',
681     );
682     is( $letter->{title}, 'a test',
683         'GetPreparedLetter should return the default notice if pref disabled but additional language exists' );
684
685 };
686
687 subtest 'SendQueuedMessages' => sub {
688
689     plan tests => 6;
690
691     t::lib::Mocks::mock_preference( 'SMSSendDriver', 'Email' );
692     t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', '');
693
694     my $patron = Koha::Patrons->find($borrowernumber);
695     $dbh->do(q|
696         INSERT INTO message_queue(borrowernumber, subject, content, message_transport_type, status, letter_code)
697         VALUES (?, 'subject', 'content', 'sms', 'pending', 'just_a_code')
698         |, undef, $borrowernumber
699     );
700     eval { C4::Letters::SendQueuedMessages(); };
701     is( $@, '', 'SendQueuedMessages should not explode if the patron does not have a sms provider set' );
702
703     my $sms_pro = $builder->build_object({ class => 'Koha::SMS::Providers', value => { domain => 'kidclamp.rocks' } });
704     $patron->set( { smsalertnumber => '5555555555', sms_provider_id => $sms_pro->id() } )->store;
705     $message_id = C4::Letters::EnqueueLetter($my_message); #using datas set around line 95 and forward
706     C4::Letters::SendQueuedMessages();
707
708     my $message = $schema->resultset('MessageQueue')->search({
709         borrowernumber => $borrowernumber,
710         status => 'sent'
711     })->next();
712
713     is( $message->to_address(), '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address not set' );
714     is(
715         $message->from_address(),
716         'from@example.com',
717         'SendQueuedMessages uses message queue item \"from address\" for SMS by email when EmailSMSSendDriverFromAddress system preference is not set'
718     );
719
720     $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber, status => 'sent'})->delete(); #clear borrower queue
721
722     t::lib::Mocks::mock_preference('EmailSMSSendDriverFromAddress', 'override@example.com');
723
724     $message_id = C4::Letters::EnqueueLetter($my_message);
725     C4::Letters::SendQueuedMessages();
726
727     $message = $schema->resultset('MessageQueue')->search({
728         borrowernumber => $borrowernumber,
729         status => 'sent'
730     })->next();
731
732     is(
733         $message->from_address(),
734         'override@example.com',
735         'SendQueuedMessages uses EmailSMSSendDriverFromAddress value for SMS by email when EmailSMSSendDriverFromAddress is set'
736     );
737
738     $schema->resultset('MessageQueue')->search({borrowernumber => $borrowernumber,status => 'sent'})->delete(); #clear borrower queue
739     $my_message->{to_address} = 'fixme@kidclamp.iswrong';
740     $message_id = C4::Letters::EnqueueLetter($my_message);
741
742     my $number_attempted = C4::Letters::SendQueuedMessages({
743         borrowernumber => -1, # -1 still triggers the borrowernumber condition
744         letter_code    => 'PASSWORD_RESET',
745     });
746     is ( $number_attempted, 0, 'There were no password reset messages for SendQueuedMessages to attempt.' );
747
748     C4::Letters::SendQueuedMessages();
749     my $sms_message_address = $schema->resultset('MessageQueue')->search({
750         borrowernumber => $borrowernumber,
751         status => 'sent'
752     })->next()->to_address();
753     is( $sms_message_address, '5555555555@kidclamp.rocks', 'SendQueuedMessages populates the to address correctly for SMS by email when to_address is set incorrectly' );
754
755 };
756
757 subtest 'get_item_content' => sub {
758     plan tests => 2;
759
760     t::lib::Mocks::mock_preference('dateformat', 'metric');
761     t::lib::Mocks::mock_preference('timeformat', '24hr');
762     my @items = (
763         {date_due => '2041-01-01 12:34', title => 'a first title', barcode => 'a_first_barcode', author => 'a_first_author', itemnumber => 1 },
764         {date_due => '2042-01-02 23:45', title => 'a second title', barcode => 'a_second_barcode', author => 'a_second_author', itemnumber => 2 },
765     );
766     my @item_content_fields = qw( date_due title barcode author itemnumber );
767
768     my $items_content;
769     for my $item ( @items ) {
770         $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields } );
771     }
772
773     my $expected_items_content = <<EOF;
774 01/01/2041 12:34\ta first title\ta_first_barcode\ta_first_author\t1
775 02/01/2042 23:45\ta second title\ta_second_barcode\ta_second_author\t2
776 EOF
777     is( $items_content, $expected_items_content, 'get_item_content should return correct items info with time (default)' );
778
779
780     $items_content = q||;
781     for my $item ( @items ) {
782         $items_content .= C4::Letters::get_item_content( { item => $item, item_content_fields => \@item_content_fields, dateonly => 1, } );
783     }
784
785     $expected_items_content = <<EOF;
786 01/01/2041\ta first title\ta_first_barcode\ta_first_author\t1
787 02/01/2042\ta second title\ta_second_barcode\ta_second_author\t2
788 EOF
789     is( $items_content, $expected_items_content, 'get_item_content should return correct items info without time (if dateonly => 1)' );
790 };
791
792 subtest 'Test limit parameter for SendQueuedMessages' => sub {
793     plan tests => 3;
794
795     my $dbh = C4::Context->dbh;
796
797     my $borrowernumber = Koha::Patron->new({
798         firstname    => 'Jane',
799         surname      => 'Smith',
800         categorycode => $patron_category,
801         branchcode   => $library->{branchcode},
802         dateofbirth  => $date,
803         smsalertnumber => undef,
804     })->store->borrowernumber;
805
806     $dbh->do(q|DELETE FROM message_queue|);
807     $my_message = {
808         'letter' => {
809             'content'      => 'a message',
810             'metadata'     => 'metadata',
811             'code'         => 'TEST_MESSAGE',
812             'content_type' => 'text/plain',
813             'title'        => 'message title'
814         },
815         'borrowernumber'         => $borrowernumber,
816         'to_address'             => undef,
817         'message_transport_type' => 'sms',
818         'from_address'           => 'from@example.com'
819     };
820     C4::Letters::EnqueueLetter($my_message);
821     C4::Letters::EnqueueLetter($my_message);
822     C4::Letters::EnqueueLetter($my_message);
823     C4::Letters::EnqueueLetter($my_message);
824     C4::Letters::EnqueueLetter($my_message);
825     my $messages_processed = C4::Letters::SendQueuedMessages( { limit => 1 } );
826     is( $messages_processed, 1,
827         'Processed 1 message with limit of 1 and 5 unprocessed messages' );
828     $messages_processed = C4::Letters::SendQueuedMessages( { limit => 2 } );
829     is( $messages_processed, 2,
830         'Processed 2 message with limit of 2 and 4 unprocessed messages' );
831     $messages_processed = C4::Letters::SendQueuedMessages( { limit => 3 } );
832     is( $messages_processed, 2,
833         'Processed 2 message with limit of 3 and 2 unprocessed messages' );
834 };