Bug 21999: Update Tests to reflect new return value of AddIssue
[koha.git] / t / db_dependent / Letters / TemplateToolkit.t
1 #!/usr/bin/perl
2
3 # This file is part of Koha.
4 #
5 # Copyright (C) 2016 ByWater Solutions
6 # Copyright (C) 2017 Koha Development Team
7 #
8 # Koha is free software; you can redistribute it and/or modify it
9 # under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # Koha is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with Koha; if not, see <http://www.gnu.org/licenses>.
20
21 use Modern::Perl;
22 use Test::More tests => 18;
23 use Test::MockModule;
24 use Test::Warn;
25
26 use MARC::Record;
27
28 use t::lib::TestBuilder;
29 use t::lib::Mocks;
30
31 use C4::Circulation;
32 use C4::Letters;
33 use C4::Members;
34 use C4::Biblio;
35 use Koha::Database;
36 use Koha::DateUtils;
37 use Koha::ArticleRequests;
38 use Koha::Biblio;
39 use Koha::Biblioitem;
40 use Koha::Item;
41 use Koha::Hold;
42 use Koha::NewsItem;
43 use Koha::Serial;
44 use Koha::Subscription;
45 use Koha::Suggestion;
46 use Koha::Checkout;
47 use Koha::Notice::Messages;
48 use Koha::Notice::Templates;
49 use Koha::Patron::Modification;
50
51 my $schema = Koha::Database->schema;
52 $schema->storage->txn_begin();
53
54 my $builder = t::lib::TestBuilder->new();
55
56 my $dbh = C4::Context->dbh;
57 $dbh->{RaiseError} = 1;
58
59 $dbh->do(q|DELETE FROM letter|);
60
61 my $now_value       = DateTime->now();
62 my $mocked_datetime = Test::MockModule->new('DateTime');
63 $mocked_datetime->mock( 'now', sub { return $now_value->clone; } );
64
65 my $library = $builder->build( { source => 'Branch' } );
66 my $patron  = $builder->build( { source => 'Borrower' } );
67 my $patron2 = $builder->build( { source => 'Borrower' } );
68
69 my $biblio = Koha::Biblio->new(
70     {
71         title => 'Test Biblio'
72     }
73 )->store();
74
75 my $biblioitem = Koha::Biblioitem->new(
76     {
77         biblionumber => $biblio->id()
78     }
79 )->store();
80
81 my $item = Koha::Item->new(
82     {
83         biblionumber     => $biblio->id(),
84         biblioitemnumber => $biblioitem->id()
85     }
86 )->store();
87
88 my $hold = Koha::Hold->new(
89     {
90         borrowernumber => $patron->{borrowernumber},
91         biblionumber   => $biblio->id()
92     }
93 )->store();
94
95 my $news         = Koha::NewsItem->new({ title => 'a news title', content => 'a news content'})->store();
96 my $serial       = Koha::Serial->new()->store();
97 my $subscription = Koha::Subscription->new()->store();
98 my $suggestion   = Koha::Suggestion->new()->store();
99 my $checkout     = Koha::Checkout->new( { itemnumber => $item->id() } )->store();
100 my $modification = Koha::Patron::Modification->new( { verification_token => "TEST" } )->store();
101
102 my $prepared_letter;
103
104 my $sth =
105   $dbh->prepare(q{INSERT INTO letter (module, code, name, title, content) VALUES ('test',?,'Test','Test',?)});
106
107 $sth->execute( "TEST_PATRON", "[% borrower.id %]" );
108 $prepared_letter = GetPreparedLetter(
109     (
110         module      => 'test',
111         letter_code => 'TEST_PATRON',
112         tables      => {
113             borrowers => $patron->{borrowernumber},
114         },
115     )
116 );
117 is( $prepared_letter->{content}, $patron->{borrowernumber}, 'Patron object used correctly with scalar' );
118
119 $prepared_letter = GetPreparedLetter(
120     (
121         module      => 'test',
122         letter_code => 'TEST_PATRON',
123         tables      => {
124             borrowers => $patron,
125         },
126     )
127 );
128 is( $prepared_letter->{content}, $patron->{borrowernumber}, 'Patron object used correctly with hashref' );
129
130 $prepared_letter = GetPreparedLetter(
131     (
132         module      => 'test',
133         letter_code => 'TEST_PATRON',
134         tables      => {
135             borrowers => [ $patron->{borrowernumber} ],
136         },
137     )
138 );
139 is( $prepared_letter->{content}, $patron->{borrowernumber}, 'Patron object used correctly with arrayref' );
140
141 $sth->execute( "TEST_BIBLIO", "[% biblio.id %]" );
142 $prepared_letter = GetPreparedLetter(
143     (
144         module      => 'test',
145         letter_code => 'TEST_BIBLIO',
146         tables      => {
147             biblio => $biblio->id(),
148         },
149     )
150 );
151 is( $prepared_letter->{content}, $biblio->id, 'Biblio object used correctly' );
152
153 $sth->execute( "TEST_LIBRARY", "[% branch.id %]" );
154 $prepared_letter = GetPreparedLetter(
155     (
156         module      => 'test',
157         letter_code => 'TEST_LIBRARY',
158         tables      => {
159             branches => $library->{branchcode}
160         },
161     )
162 );
163 is( $prepared_letter->{content}, $library->{branchcode}, 'Library object used correctly' );
164
165 $sth->execute( "TEST_ITEM", "[% item.id %]" );
166 $prepared_letter = GetPreparedLetter(
167     (
168         module      => 'test',
169         letter_code => 'TEST_ITEM',
170         tables      => {
171             items => $item->id()
172         },
173     )
174 );
175 is( $prepared_letter->{content}, $item->id(), 'Item object used correctly' );
176
177 $sth->execute( "TEST_NEWS", "[% news.id %]" );
178 $prepared_letter = GetPreparedLetter(
179     (
180         module      => 'test',
181         letter_code => 'TEST_NEWS',
182         tables      => {
183             opac_news => $news->id()
184         },
185     )
186 );
187 is( $prepared_letter->{content}, $news->id(), 'News object used correctly' );
188
189 $sth->execute( "TEST_HOLD", "[% hold.id %]" );
190 $prepared_letter = GetPreparedLetter(
191     (
192         module      => 'test',
193         letter_code => 'TEST_HOLD',
194         tables      => {
195             reserves => { borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio->id() },
196         },
197     )
198 );
199 is( $prepared_letter->{content}, $hold->id(), 'Hold object used correctly' );
200
201 eval {
202     $prepared_letter = GetPreparedLetter(
203         (
204             module      => 'test',
205             letter_code => 'TEST_HOLD',
206             tables      => {
207                 reserves => [ $patron->{borrowernumber}, $biblio->id() ],
208             },
209         )
210     )
211 };
212 my $croak = $@;
213 like( $croak, qr{^Multiple foreign keys \(table reserves\) should be passed using an hashref.*}, "GetPreparedLetter should not be called with arrayref for multiple FK" );
214
215 # Bug 16942
216 $prepared_letter = GetPreparedLetter(
217     (
218         module      => 'test',
219         letter_code => 'TEST_HOLD',
220         tables      => {
221             'branches'    => $library,
222             'borrowers'   => $patron,
223             'biblio'      => $biblio->id,
224             'biblioitems' => $biblioitem->id,
225             'reserves'    => $hold->unblessed,
226             'items'       => $hold->itemnumber,
227         }
228     )
229 );
230 is( $prepared_letter->{content}, $hold->id(), 'Hold object used correctly' );
231
232 $sth->execute( "TEST_SERIAL", "[% serial.id %]" );
233 $prepared_letter = GetPreparedLetter(
234     (
235         module      => 'test',
236         letter_code => 'TEST_SERIAL',
237         tables      => {
238             serial => $serial->id()
239         },
240     )
241 );
242 is( $prepared_letter->{content}, $serial->id(), 'Serial object used correctly' );
243
244 $sth->execute( "TEST_SUBSCRIPTION", "[% subscription.id %]" );
245 $prepared_letter = GetPreparedLetter(
246     (
247         module      => 'test',
248         letter_code => 'TEST_SUBSCRIPTION',
249         tables      => {
250             subscription => $subscription->id()
251         },
252     )
253 );
254 is( $prepared_letter->{content}, $subscription->id(), 'Subscription object used correctly' );
255
256 $sth->execute( "TEST_SUGGESTION", "[% suggestion.id %]" );
257 $prepared_letter = GetPreparedLetter(
258     (
259         module      => 'test',
260         letter_code => 'TEST_SUGGESTION',
261         tables      => {
262             suggestions => $suggestion->id()
263         },
264     )
265 );
266 is( $prepared_letter->{content}, $suggestion->id(), 'Suggestion object used correctly' );
267
268 $sth->execute( "TEST_ISSUE", "[% checkout.id %]" );
269 $prepared_letter = GetPreparedLetter(
270     (
271         module      => 'test',
272         letter_code => 'TEST_ISSUE',
273         tables      => {
274             issues => $item->id()
275         },
276     )
277 );
278 is( $prepared_letter->{content}, $checkout->id(), 'Checkout object used correctly' );
279
280 $sth->execute( "TEST_MODIFICATION", "[% patron_modification.id %]" );
281 $prepared_letter = GetPreparedLetter(
282     (
283         module      => 'test',
284         letter_code => 'TEST_MODIFICATION',
285         tables      => {
286             borrower_modifications => $modification->verification_token,
287         },
288     )
289 );
290 is( $prepared_letter->{content}, $modification->id(), 'Patron modification object used correctly' );
291
292 subtest 'regression tests' => sub {
293     plan tests => 8;
294
295     my $library = $builder->build( { source => 'Branch' } );
296     my $patron  = $builder->build( { source => 'Borrower' } );
297     my $biblio1 = Koha::Biblio->new({title => 'Test Biblio 1', author => 'An author', })->store->unblessed;
298     my $biblioitem1 = Koha::Biblioitem->new({biblionumber => $biblio1->{biblionumber}})->store()->unblessed;
299     my $item1 = Koha::Item->new(
300         {
301             biblionumber     => $biblio1->{biblionumber},
302             biblioitemnumber => $biblioitem1->{biblioitemnumber},
303             barcode          => 'a_t_barcode',
304             homebranch       => $library->{branchcode},
305             holdingbranch    => $library->{branchcode},
306             itype            => 'BK',
307             itemcallnumber   => 'itemcallnumber1',
308         }
309     )->store->unblessed;
310     my $biblio2 = Koha::Biblio->new({title => 'Test Biblio 2'})->store->unblessed;
311     my $biblioitem2 = Koha::Biblioitem->new({biblionumber => $biblio2->{biblionumber}})->store()->unblessed;
312     my $item2 = Koha::Item->new(
313         {
314             biblionumber     => $biblio2->{biblionumber},
315             biblioitemnumber => $biblioitem2->{biblioitemnumber},
316             barcode          => 'another_t_barcode',
317             homebranch       => $library->{branchcode},
318             holdingbranch    => $library->{branchcode},
319             itype            => 'BK',
320             itemcallnumber   => 'itemcallnumber2',
321         }
322     )->store->unblessed;
323     my $biblio3 = Koha::Biblio->new({title => 'Test Biblio 3'})->store->unblessed;
324     my $biblioitem3 = Koha::Biblioitem->new({biblionumber => $biblio3->{biblionumber}})->store()->unblessed;
325     my $item3 = Koha::Item->new(
326         {
327             biblionumber     => $biblio3->{biblionumber},
328             biblioitemnumber => $biblioitem3->{biblioitemnumber},
329             barcode          => 'another_t_barcode_3',
330             homebranch       => $library->{branchcode},
331             holdingbranch    => $library->{branchcode},
332             itype            => 'BK',
333             itemcallnumber   => 'itemcallnumber3',
334         }
335     )->store->unblessed;
336
337     t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
338
339     subtest 'ACQ_NOTIF_ON_RECEIV ' => sub {
340         plan tests => 1;
341         my $code = 'ACQ_NOTIF_ON_RECEIV';
342         my $branchcode = $library->{branchcode};
343         my $order = $builder->build({ source => 'Aqorder' });
344
345         my $template = q|
346 Dear <<borrowers.firstname>> <<borrowers.surname>>,
347 The order <<aqorders.ordernumber>> (<<biblio.title>>) has been received.
348 Your library.
349         |;
350         my $params = { code => $code, branchcode => $branchcode, tables => { branches => $library, borrowers => $patron, biblio => $biblio1, aqorders => $order } };
351         my $letter = process_letter( { template => $template, %$params });
352         my $tt_template = q|
353 Dear [% borrower.firstname %] [% borrower.surname %],
354 The order [% order.ordernumber %] ([% biblio.title %]) has been received.
355 Your library.
356         |;
357         my $tt_letter = process_letter( { template => $tt_template, %$params });
358
359         is( $tt_letter->{content}, $letter->{content}, 'Verified letter content' );
360     };
361
362     subtest 'AR_*' => sub {
363         plan tests => 2;
364         my $code = 'AR_CANCELED';
365         my $branchcode = $library->{branchcode};
366
367         my $template = q|
368 <<borrowers.firstname>> <<borrowers.surname>> (<<borrowers.cardnumber>>)
369
370 Your request for an article from <<biblio.title>> (<<items.barcode>>) has been canceled for the following reason:
371
372 <<article_requests.notes>>
373
374 Article requested:
375 Title: <<article_requests.title>>
376 Author: <<article_requests.author>>
377 Volume: <<article_requests.volume>>
378 Issue: <<article_requests.issue>>
379 Date: <<article_requests.date>>
380 Pages: <<article_requests.pages>>
381 Chapters: <<article_requests.chapters>>
382 Notes: <<article_requests.patron_notes>>
383         |;
384         reset_template( { template => $template, code => $code, module => 'circulation' } );
385         my $article_request = $builder->build({ source => 'ArticleRequest' });
386         Koha::ArticleRequests->find( $article_request->{id} )->cancel;
387         my $letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
388
389         my $tt_template = q|
390 [% borrower.firstname %] [% borrower.surname %] ([% borrower.cardnumber %])
391
392 Your request for an article from [% biblio.title %] ([% item.barcode %]) has been canceled for the following reason:
393
394 [% article_request.notes %]
395
396 Article requested:
397 Title: [% article_request.title %]
398 Author: [% article_request.author %]
399 Volume: [% article_request.volume %]
400 Issue: [% article_request.issue %]
401 Date: [% article_request.date %]
402 Pages: [% article_request.pages %]
403 Chapters: [% article_request.chapters %]
404 Notes: [% article_request.patron_notes %]
405         |;
406         reset_template( { template => $tt_template, code => $code, module => 'circulation' } );
407         Koha::ArticleRequests->find( $article_request->{id} )->cancel;
408         my $tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
409         is( $tt_letter->content, $letter->content, 'Compare AR_* notices' );
410         isnt( $tt_letter->message_id, $letter->message_id, 'Comparing AR_* notices should compare 2 different messages' );
411     };
412
413     subtest 'CHECKOUT+CHECKIN' => sub {
414         plan tests => 4;
415
416         my $checkout_code = 'CHECKOUT';
417         my $checkin_code = 'CHECKIN';
418
419         my $dbh = C4::Context->dbh;
420         # Enable notification for CHECKOUT - Things are hardcoded here but should work with default data
421         $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 6 );
422         my $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
423         $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
424         # Enable notification for CHECKIN - Things are hardcoded here but should work with default data
425         $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 5 );
426         $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
427         $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
428
429         # historic syntax
430         my $checkout_template = q|
431 The following items have been checked out:
432 ----
433 <<biblio.title>>
434 ----
435 Thank you for visiting <<branches.branchname>>.
436 |;
437         reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
438         my $checkin_template = q[
439 The following items have been checked out:
440 ----
441 <<biblio.title>> was due on <<old_issues.date_due | dateonly>>
442 ----
443 Thank you for visiting <<branches.branchname>>.
444 ];
445         reset_template( { template => $checkin_template, code => $checkin_code, module => 'circulation' } );
446
447         C4::Circulation::AddIssue( $patron, $item1->{barcode} );
448         my $first_checkout_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
449         C4::Circulation::AddIssue( $patron, $item2->{barcode} );
450         my $second_checkout_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
451
452         AddReturn( $item1->{barcode} );
453         my $first_checkin_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
454         AddReturn( $item2->{barcode} );
455         my $second_checkin_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
456
457         Koha::Notice::Messages->delete;
458
459         # TT syntax
460         $checkout_template = q|
461 The following items have been checked out:
462 ----
463 [% biblio.title %]
464 ----
465 Thank you for visiting [% branch.branchname %].
466 |;
467         reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
468         $checkin_template = q[
469 The following items have been checked out:
470 ----
471 [% biblio.title %] was due on [% old_checkout.date_due | $KohaDates %]
472 ----
473 Thank you for visiting [% branch.branchname %].
474 ];
475         reset_template( { template => $checkin_template, code => $checkin_code, module => 'circulation' } );
476
477         C4::Circulation::AddIssue( $patron, $item1->{barcode} );
478         my $first_checkout_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
479         C4::Circulation::AddIssue( $patron, $item2->{barcode} );
480         my $second_checkout_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
481
482         AddReturn( $item1->{barcode} );
483         my $first_checkin_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
484         AddReturn( $item2->{barcode} );
485         my $second_checkin_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
486
487         is( $first_checkout_tt_letter->content, $first_checkout_letter->content, 'Verify first checkout letter' );
488         is( $second_checkout_tt_letter->content, $second_checkout_letter->content, 'Verify second checkout letter' );
489         is( $first_checkin_tt_letter->content, $first_checkin_letter->content, 'Verify first checkin letter'  );
490         is( $second_checkin_tt_letter->content, $second_checkin_letter->content, 'Verify second checkin letter' );
491
492     };
493
494     subtest 'DUEDGST|count' => sub {
495         plan tests => 1;
496
497         my $code = 'DUEDGST';
498
499         my $dbh = C4::Context->dbh;
500         # Enable notification for DUEDGST - Things are hardcoded here but should work with default data
501         $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 1 );
502         my $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
503         $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
504
505         my $params = {
506             code => $code,
507             substitute => { count => 42 },
508         };
509
510         my $template = q|
511 You have <<count>> items due
512         |;
513         my $letter = process_letter( { template => $template, %$params });
514
515         my $tt_template = q|
516 You have [% count %] items due
517         |;
518         my $tt_letter = process_letter( { template => $tt_template, %$params });
519         is( $tt_letter->{content}, $letter->{content}, );
520     };
521
522     subtest 'HOLD_SLIP|dates|today' => sub {
523         plan tests => 2;
524
525         my $code = 'HOLD_SLIP';
526
527         C4::Reserves::AddReserve( $library->{branchcode}, $patron->{borrowernumber}, $biblio1->{biblionumber}, undef, undef, undef, undef, "a note", undef, $item1->{itemnumber}, 'W' );
528         C4::Reserves::AddReserve( $library->{branchcode}, $patron->{borrowernumber}, $biblio2->{biblionumber}, undef, undef, undef, undef, "another note", undef, $item2->{itemnumber} );
529
530         my $template = <<EOF;
531 <h5>Date: <<today>></h5>
532
533 <h3> Transfer to/Hold in <<branches.branchname>></h3>
534
535 <h3><<borrowers.surname>>, <<borrowers.firstname>></h3>
536
537 <ul>
538     <li><<borrowers.cardnumber>></li>
539     <li><<borrowers.phone>></li>
540     <li> <<borrowers.address>><br />
541          <<borrowers.address2>><br />
542          <<borrowers.city>>  <<borrowers.zipcode>>
543     </li>
544     <li><<borrowers.email>></li>
545 </ul>
546 <br />
547 <h3>ITEM ON HOLD</h3>
548 <h4><<biblio.title>></h4>
549 <h5><<biblio.author>></h5>
550 <ul>
551    <li><<items.barcode>></li>
552    <li><<items.itemcallnumber>></li>
553    <li><<reserves.waitingdate>></li>
554 </ul>
555 <p>Notes:
556 <pre><<reserves.reservenotes>></pre>
557 </p>
558 EOF
559
560         reset_template( { template => $template, code => $code, module => 'circulation' } );
561         my $letter_for_item1 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio1->{biblionumber} } );
562         my $letter_for_item2 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio2->{biblionumber} } );
563
564         my $tt_template = <<EOF;
565 <h5>Date: [% today | \$KohaDates with_hours => 1 %]</h5>
566
567 <h3> Transfer to/Hold in [% branch.branchname %]</h3>
568
569 <h3>[% borrower.surname %], [% borrower.firstname %]</h3>
570
571 <ul>
572     <li>[% borrower.cardnumber %]</li>
573     <li>[% borrower.phone %]</li>
574     <li> [% borrower.address %]<br />
575          [% borrower.address2 %]<br />
576          [% borrower.city %]  [% borrower.zipcode %]
577     </li>
578     <li>[% borrower.email %]</li>
579 </ul>
580 <br />
581 <h3>ITEM ON HOLD</h3>
582 <h4>[% biblio.title %]</h4>
583 <h5>[% biblio.author %]</h5>
584 <ul>
585    <li>[% item.barcode %]</li>
586    <li>[% item.itemcallnumber %]</li>
587    <li>[% hold.waitingdate | \$KohaDates %]</li>
588 </ul>
589 <p>Notes:
590 <pre>[% hold.reservenotes %]</pre>
591 </p>
592 EOF
593
594         reset_template( { template => $tt_template, code => $code, module => 'circulation' } );
595         my $tt_letter_for_item1 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio1->{biblionumber} } );
596         my $tt_letter_for_item2 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio2->{biblionumber} } );
597
598         is( $tt_letter_for_item1->{content}, $letter_for_item1->{content}, );
599         is( $tt_letter_for_item2->{content}, $letter_for_item2->{content}, );
600     };
601
602     subtest 'ISSUESLIP|checkedout|repeat' => sub {
603         plan tests => 2;
604
605         my $code = 'ISSUESLIP';
606         my $now = dt_from_string;
607         my $one_minute_ago = dt_from_string->subtract( minutes => 1 );
608
609         my $branchcode = $library->{branchcode};
610
611         Koha::News->delete;
612         my $news_item = Koha::NewsItem->new({ branchcode => $branchcode, title => "A wonderful news", content => "This is the wonderful news." })->store;
613
614         # historic syntax
615         my $template = <<EOF;
616 <h3><<branches.branchname>></h3>
617 Checked out to <<borrowers.title>> <<borrowers.firstname>> <<borrowers.initials>> <<borrowers.surname>> <br />
618 (<<borrowers.cardnumber>>) <br />
619
620 <<today>><br />
621
622 <h4>Checked Out</h4>
623 <checkedout>
624 <p>
625 <<biblio.title>> <br />
626 Barcode: <<items.barcode>><br />
627 Date due: <<issues.date_due | dateonly>><br />
628 </p>
629 </checkedout>
630
631 <h4>Overdues</h4>
632 <overdue>
633 <p>
634 <<biblio.title>> <br />
635 Barcode: <<items.barcode>><br />
636 Date due: <<issues.date_due | dateonly>><br />
637 </p>
638 </overdue>
639
640 <hr>
641
642 <h4 style="text-align: center; font-style:italic;">News</h4>
643 <news>
644 <div class="newsitem">
645 <h5 style="margin-bottom: 1px; margin-top: 1px"><b><<opac_news.title>></b></h5>
646 <p style="margin-bottom: 1px; margin-top: 1px"><<opac_news.content>></p>
647 <p class="newsfooter" style="font-size: 8pt; font-style:italic; margin-bottom: 1px; margin-top: 1px">Posted on <<opac_news.timestamp>></p>
648 <hr />
649 </div>
650 </news>
651 EOF
652
653         reset_template( { template => $template, code => $code, module => 'circulation' } );
654
655         my $checkout = C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
656         $checkout->set( { timestamp => $now, issuedate => $one_minute_ago } )->store;
657         my $first_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
658
659         $checkout = C4::Circulation::AddIssue( $patron, $item2->{barcode} ); # Add a second checkout
660         $checkout->set( { timestamp => $now, issuedate => $now } )->store;
661         my $yesterday = dt_from_string->subtract( days => 1 );
662         C4::Circulation::AddIssue( $patron, $item3->{barcode}, $yesterday ); # Add an overdue
663         my $second_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
664
665         # Cleanup
666         AddReturn( $item1->{barcode} );
667         AddReturn( $item2->{barcode} );
668         AddReturn( $item3->{barcode} );
669
670         # TT syntax
671         my $tt_template = <<EOF;
672 <h3>[% branch.branchname %]</h3>
673 Checked out to [% borrower.title %] [% borrower.firstname %] [% borrower.initials %] [% borrower.surname %] <br />
674 ([% borrower.cardnumber %]) <br />
675
676 [% today | \$KohaDates with_hours => 1 %]<br />
677
678 <h4>Checked Out</h4>
679 [% FOREACH checkout IN checkouts %]
680 [%~ SET item = checkout.item %]
681 [%~ SET biblio = checkout.item.biblio %]
682 <p>
683 [% biblio.title %] <br />
684 Barcode: [% item.barcode %]<br />
685 Date due: [% checkout.date_due | \$KohaDates %]<br />
686 </p>
687 [% END %]
688
689 <h4>Overdues</h4>
690 [% FOREACH overdue IN overdues %]
691 [%~ SET item = overdue.item %]
692 [%~ SET biblio = overdue.item.biblio %]
693 <p>
694 [% biblio.title %] <br />
695 Barcode: [% item.barcode %]<br />
696 Date due: [% overdue.date_due | \$KohaDates %]<br />
697 </p>
698 [% END %]
699
700 <hr>
701
702 <h4 style="text-align: center; font-style:italic;">News</h4>
703 [% FOREACH n IN news %]
704 <div class="newsitem">
705 <h5 style="margin-bottom: 1px; margin-top: 1px"><b>[% n.title %]</b></h5>
706 <p style="margin-bottom: 1px; margin-top: 1px">[% n.content %]</p>
707 <p class="newsfooter" style="font-size: 8pt; font-style:italic; margin-bottom: 1px; margin-top: 1px">Posted on [% n.timestamp | \$KohaDates %]</p>
708 <hr />
709 </div>
710 [% END %]
711 EOF
712
713         reset_template( { template => $tt_template, code => $code, module => 'circulation' } );
714
715         $checkout = C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
716         $checkout->set( { timestamp => $now, issuedate => $one_minute_ago } )->store;
717         my $first_tt_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
718
719         $checkout = C4::Circulation::AddIssue( $patron, $item2->{barcode} ); # Add a second checkout
720         $checkout->set( { timestamp => $now, issuedate => $now } )->store;
721         C4::Circulation::AddIssue( $patron, $item3->{barcode}, $yesterday ); # Add an overdue
722         my $second_tt_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
723
724         # There is too many line breaks generated by the historic syntax
725         $second_slip->{content} =~ s|</p>\n\n\n<p>|</p>\n\n<p>|s;
726
727         is( $first_tt_slip->{content}, $first_slip->{content}, );
728         is( $second_tt_slip->{content}, $second_slip->{content}, );
729
730         # Cleanup
731         AddReturn( $item1->{barcode} );
732         AddReturn( $item2->{barcode} );
733         AddReturn( $item3->{barcode} );
734     };
735
736     subtest 'ODUE|items.content|item' => sub {
737         plan tests => 1;
738
739         my $code = 'ODUE';
740
741         my $branchcode = $library->{branchcode};
742
743         # historic syntax
744         # FIXME items.fine does not work with TT notices
745         # See bug 17976
746         # <item> should contain Fine: <<items.fine>></item>
747         my $template = <<EOF;
748 Dear <<borrowers.firstname>> <<borrowers.surname>>,
749
750 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.
751
752 <<branches.branchname>>
753 <<branches.branchaddress1>>
754 <<branches.branchaddress2>> <<branches.branchaddress3>>
755 Phone: <<branches.branchphone>>
756 Fax: <<branches.branchfax>>
757 Email: <<branches.branchemail>>
758
759 If you have registered a password with the library, and you have a renewal available, you may renew online. If an item becomes more than 30 days overdue, you will be unable to use your library card until the item is returned.
760
761 The following item(s) is/are currently overdue:
762
763 <item>"<<biblio.title>>" by <<biblio.author>>, <<items.itemcallnumber>>, Barcode: <<items.barcode>></item>
764
765 <<items.content>>
766
767 Thank-you for your prompt attention to this matter.
768
769 <<branches.branchname>> Staff
770 EOF
771
772         reset_template( { template => $template, code => $code, module => 'circulation' } );
773
774         my $yesterday = dt_from_string->subtract( days => 1 );
775         my $two_days_ago = dt_from_string->subtract( days => 2 );
776         my $issue1 = C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
777         my $issue2 = C4::Circulation::AddIssue( $patron, $item2->{barcode}, $yesterday ); # Add an first overdue
778         my $issue3 = C4::Circulation::AddIssue( $patron, $item3->{barcode}, $two_days_ago ); # Add an second overdue
779         $issue1 = $issue1->unblessed;
780         $issue2 = $issue2->unblessed;
781         $issue3 = $issue3->unblessed;
782
783         # For items.content
784         my @item_fields = qw( date_due title barcode author itemnumber );
785         my $items_content = C4::Letters::get_item_content( { item => { %$item1, %$biblio1, %$issue1 }, item_content_fields => \@item_fields, dateonly => 1 } );
786           $items_content .= C4::Letters::get_item_content( { item => { %$item2, %$biblio2, %$issue2 }, item_content_fields => \@item_fields, dateonly => 1 } );
787           $items_content .= C4::Letters::get_item_content( { item => { %$item3, %$biblio3, %$issue3 }, item_content_fields => \@item_fields, dateonly => 1 } );
788
789         my @items = ( $item1, $item2, $item3 );
790         my $letter = C4::Overdues::parse_overdues_letter(
791             {
792                 letter_code => $code,
793                 borrowernumber => $patron->{borrowernumber},
794                 branchcode  => $library->{branchcode},
795                 items       => \@items,
796                 substitute  => {
797                     bib                    => $library->{branchname},
798                     'items.content'        => $items_content,
799                     count                  => scalar( @items ),
800                     message_transport_type => 'email',
801                 }
802             }
803         );
804
805         # Cleanup
806         AddReturn( $item1->{barcode} );
807         AddReturn( $item2->{barcode} );
808         AddReturn( $item3->{barcode} );
809
810
811         # historic syntax
812         my $tt_template = <<EOF;
813 Dear [% borrower.firstname %] [% borrower.surname %],
814
815 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.
816
817 [% branch.branchname %]
818 [% branch.branchaddress1 %]
819 [% branch.branchaddress2 %] [% branch.branchaddress3 %]
820 Phone: [% branch.branchphone %]
821 Fax: [% branch.branchfax %]
822 Email: [% branch.branchemail %]
823
824 If you have registered a password with the library, and you have a renewal available, you may renew online. If an item becomes more than 30 days overdue, you will be unable to use your library card until the item is returned.
825
826 The following item(s) is/are currently overdue:
827
828 [% FOREACH overdue IN overdues %]
829 [%~ SET item = overdue.item ~%]
830 "[% item.biblio.title %]" by [% item.biblio.author %], [% item.itemcallnumber %], Barcode: [% item.barcode %]
831 [% END %]
832 [% FOREACH overdue IN overdues %]
833 [%~ SET item = overdue.item ~%]
834 [% overdue.date_due | \$KohaDates %]\t[% item.biblio.title %]\t[% item.barcode %]\t[% item.biblio.author %]\t[% item.itemnumber %]
835 [% END %]
836
837 Thank-you for your prompt attention to this matter.
838
839 [% branch.branchname %] Staff
840 EOF
841
842         reset_template( { template => $tt_template, code => $code, module => 'circulation' } );
843
844         C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
845         C4::Circulation::AddIssue( $patron, $item2->{barcode}, $yesterday ); # Add an first overdue
846         C4::Circulation::AddIssue( $patron, $item3->{barcode}, $two_days_ago ); # Add an second overdue
847
848         my $tt_letter = C4::Overdues::parse_overdues_letter(
849             {
850                 letter_code => $code,
851                 borrowernumber => $patron->{borrowernumber},
852                 branchcode  => $library->{branchcode},
853                 items       => \@items,
854                 substitute  => {
855                     bib                    => $library->{branchname},
856                     'items.content'        => $items_content,
857                     count                  => scalar( @items ),
858                     message_transport_type => 'email',
859                 }
860             }
861         );
862
863         is( $tt_letter->{content}, $letter->{content}, );
864     };
865
866     subtest 'Bug 19743 - Header and Footer should be updated on each item for checkin / checkout / renewal notices' => sub {
867         plan tests => 8;
868
869         my $checkout_code = 'CHECKOUT';
870         my $checkin_code = 'CHECKIN';
871
872         my $dbh = C4::Context->dbh;
873         $dbh->do("DELETE FROM letter");
874         $dbh->do("DELETE FROM issues");
875         $dbh->do("DELETE FROM message_queue");
876
877         # Enable notification for CHECKOUT - Things are hardcoded here but should work with default data
878         $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 6 );
879         my $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
880         $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
881         # Enable notification for CHECKIN - Things are hardcoded here but should work with default data
882         $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 5 );
883         $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
884         $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
885
886         my $checkout_template = q|
887 <<branches.branchname>>
888 ----
889 ----
890 |;
891         reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
892         my $checkin_template = q[
893 <<branches.branchname>>
894 ----
895 ----
896 ];
897         reset_template( { template => $checkin_template, code => $checkin_code, module => 'circulation' } );
898
899         my $issue = C4::Circulation::AddIssue( $patron, $item1->{barcode} );
900         my $first_checkout_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
901
902         my $library_object = Koha::Libraries->find( $issue->branchcode );
903         my $old_branchname = $library_object->branchname;
904         my $new_branchname = "Kyle M Hall Memorial Library";
905
906         # Change branch name for second checkout notice
907         $library_object->branchname($new_branchname);
908         $library_object->store();
909
910         C4::Circulation::AddIssue( $patron, $item2->{barcode} );
911         my $second_checkout_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
912
913         # Restore old name for first checkin notice
914         $library_object->branchname( $old_branchname );
915         $library_object->store();
916
917         AddReturn( $item1->{barcode} );
918         my $first_checkin_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
919
920         # Change branch name for second checkin notice
921         $library_object->branchname($new_branchname);
922         $library_object->store();
923
924         AddReturn( $item2->{barcode} );
925         my $second_checkin_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
926
927         # Restore old name for first TT checkout notice
928         $library_object->branchname( $old_branchname );
929         $library_object->store();
930
931         Koha::Notice::Messages->delete;
932
933         # TT syntax
934         $checkout_template = q|
935 [% branch.branchname %]
936 ----
937 ----
938 |;
939         reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
940         $checkin_template = q[
941 [% branch.branchname %]
942 ----
943 ----
944 ];
945         reset_template( { template => $checkin_template, code => $checkin_code, module => 'circulation' } );
946
947         C4::Circulation::AddIssue( $patron, $item1->{barcode} );
948         my $first_checkout_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
949
950         # Change branch name for second checkout notice
951         $library_object->branchname($new_branchname);
952         $library_object->store();
953
954         C4::Circulation::AddIssue( $patron, $item2->{barcode} );
955         my $second_checkout_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
956
957         # Restore old name for first checkin notice
958         $library_object->branchname( $old_branchname );
959         $library_object->store();
960
961         AddReturn( $item1->{barcode} );
962         my $first_checkin_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
963 #
964         # Change branch name for second checkin notice
965         $library_object->branchname($new_branchname);
966         $library_object->store();
967
968         AddReturn( $item2->{barcode} );
969         my $second_checkin_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
970
971         my $first_letter = qq[
972 $old_branchname
973 ];
974         my $second_letter = qq[
975 $new_branchname
976 ];
977
978
979         is( $first_checkout_letter->content, $first_letter, 'Verify first checkout letter' );
980         is( $second_checkout_letter->content, $second_letter, 'Verify second checkout letter' );
981         is( $first_checkin_letter->content, $first_letter, 'Verify first checkin letter'  );
982         is( $second_checkin_letter->content, $second_letter, 'Verify second checkin letter' );
983
984         is( $first_checkout_tt_letter->content, $first_letter, 'Verify TT first checkout letter' );
985         is( $second_checkout_tt_letter->content, $second_letter, 'Verify TT second checkout letter' );
986         is( $first_checkin_tt_letter->content, $first_letter, 'Verify TT first checkin letter'  );
987         is( $second_checkin_tt_letter->content, $second_letter, 'Verify TT second checkin letter' );
988     };
989
990 };
991
992 subtest 'loops' => sub {
993     plan tests => 2;
994     my $code = "TEST";
995     my $module = "TEST";
996
997     subtest 'primary key is AI' => sub {
998         plan tests => 1;
999         my $patron_1 = $builder->build({ source => 'Borrower' });
1000         my $patron_2 = $builder->build({ source => 'Borrower' });
1001
1002         my $template = q|[% FOREACH patron IN borrowers %][% patron.surname %][% END %]|;
1003         reset_template( { template => $template, code => $code, module => $module } );
1004         my $letter = GetPreparedLetter( module => $module, letter_code => $code, loops => { borrowers => [ $patron_1->{borrowernumber}, $patron_2->{borrowernumber} ] } );
1005         my $expected_letter = join '', ( $patron_1->{surname}, $patron_2->{surname} );
1006         is( $letter->{content}, $expected_letter, );
1007     };
1008
1009     subtest 'foreign key is used' => sub {
1010         plan tests => 1;
1011         my $patron_1 = $builder->build({ source => 'Borrower' });
1012         my $patron_2 = $builder->build({ source => 'Borrower' });
1013         my $checkout_1 = $builder->build({ source => 'Issue', value => { borrowernumber => $patron_1->{borrowernumber} } } );
1014         my $checkout_2 = $builder->build({ source => 'Issue', value => { borrowernumber => $patron_1->{borrowernumber} } } );
1015
1016         my $template = q|[% FOREACH checkout IN checkouts %][% checkout.issue_id %][% END %]|;
1017         reset_template( { template => $template, code => $code, module => $module } );
1018         my $letter = GetPreparedLetter( module => $module, letter_code => $code, loops => { issues => [ $checkout_1->{itemnumber}, $checkout_2->{itemnumber} ] } );
1019         my $expected_letter = join '', ( $checkout_1->{issue_id}, $checkout_2->{issue_id} );
1020         is( $letter->{content}, $expected_letter, );
1021     };
1022 };
1023
1024 subtest 'add_tt_filters' => sub {
1025     plan tests => 1;
1026     my $code   = "TEST";
1027     my $module = "TEST";
1028
1029     my $patron = $builder->build_object(
1030         {
1031             class => 'Koha::Patrons',
1032             value => { surname => "with_punctuation_" }
1033         }
1034     );
1035     my $biblio = $builder->build_object(
1036         { class => 'Koha::Biblios', value => { title => "with_punctuation_" } }
1037     );
1038     my $biblioitem = $builder->build_object(
1039         {
1040             class => 'Koha::Biblioitems',
1041             value => {
1042                 biblionumber => $biblio->biblionumber,
1043                 isbn         => "with_punctuation_"
1044             }
1045         }
1046     );
1047
1048     my $template = q|patron=[% borrower.surname %];biblio=[% biblio.title %];biblioitems=[% biblioitem.isbn %]|;
1049     reset_template( { template => $template, code => $code, module => $module } );
1050     my $letter = GetPreparedLetter(
1051         module      => $module,
1052         letter_code => $code,
1053         tables      => {
1054             borrowers   => $patron->borrowernumber,
1055             biblio      => $biblio->biblionumber,
1056             biblioitems => $biblioitem->biblioitemnumber
1057         }
1058     );
1059     my $expected_letter = q|patron=with_punctuation_;biblio=with_punctuation;biblioitems=with_punctuation|;
1060     is( $letter->{content}, $expected_letter, "Pre-processing should call TT plugin to remove punctuation if table is biblio or biblioitems");
1061 };
1062
1063
1064 sub reset_template {
1065     my ( $params ) = @_;
1066     my $template   = $params->{template};
1067     my $code       = $params->{code};
1068     my $module     = $params->{module} || 'test_module';
1069
1070     Koha::Notice::Templates->search( { code => $code } )->delete;
1071     Koha::Notice::Template->new(
1072         {
1073             module                 => $module,
1074             code                   => $code,
1075             branchcode             => '',
1076             name                   => $code,
1077             title                  => $code,
1078             message_transport_type => 'email',
1079             content                => $template
1080         }
1081     )->store;
1082 }
1083
1084 sub process_letter {
1085     my ($params)   = @_;
1086     my $template   = $params->{template};
1087     my $tables     = $params->{tables};
1088     my $substitute = $params->{substitute};
1089     my $code       = $params->{code};
1090     my $module     = $params->{module} || 'test_module';
1091     my $branchcode = $params->{branchcode};
1092
1093     reset_template( $params );
1094
1095     my $letter = C4::Letters::GetPreparedLetter(
1096         module      => $module,
1097         letter_code => $code,
1098         branchcode  => '',
1099         tables      => $tables,
1100         substitute  => $substitute,
1101     );
1102     return $letter;
1103 }