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