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