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