8d049423c6cb27251bb57038b58002bd41fa6c9d
[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 => 28;
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
58 $dbh->do(q|DELETE FROM letter|);
59
60 my $now_value       = dt_from_string();
61 my $mocked_datetime = Test::MockModule->new('DateTime');
62 $mocked_datetime->mock( 'now', sub { return $now_value->clone; } );
63
64 my $library = $builder->build( { source => 'Branch' } );
65 my $patron  = $builder->build( { source => 'Borrower' } );
66 my $patron2 = $builder->build( { source => 'Borrower' } );
67
68 my $item = $builder->build_sample_item();
69 my $hold = $builder->build_object(
70     {
71         class => 'Koha::Holds',
72         value => {
73             borrowernumber => $patron->{borrowernumber},
74             biblionumber   => $item->biblionumber
75         }
76     }
77 );
78
79 my $news = $builder->build_object(
80     {
81         class => 'Koha::News',
82         value => { title => 'a news title', content => 'a news content' }
83     }
84 );
85 my $serial       = $builder->build_object( { class => 'Koha::Serials' } );
86 my $subscription = $builder->build_object( { class => 'Koha::Subscriptions' } );
87 my $suggestion   = $builder->build_object( { class => 'Koha::Suggestions' } );
88 my $checkout     = $builder->build_object(
89     { class => 'Koha::Checkouts', value => { itemnumber => $item->id } } );
90 my $modification = $builder->build_object(
91     {
92         class => 'Koha::Patron::Modifications',
93         value => {
94             verification_token => "TEST",
95             changed_fields     => 'firstname,surname'
96         }
97     }
98 );
99
100 my $prepared_letter;
101
102 my $sth =
103   $dbh->prepare(q{INSERT INTO letter (module, code, name, title, content) VALUES ('test',?,'Test',?,?)});
104
105 $sth->execute( "TEST_PATRON", "[% borrower.firstname %]", "[% borrower.id %]" );
106 $prepared_letter = GetPreparedLetter(
107     (
108         module      => 'test',
109         letter_code => 'TEST_PATRON',
110         tables      => {
111             borrowers => $patron->{borrowernumber},
112         },
113     )
114 );
115 is( $prepared_letter->{content}, $patron->{borrowernumber}, 'Patron object used correctly with scalar for content' );
116 is( $prepared_letter->{title}, $patron->{firstname}, 'Patron object used correctly with scalar for title' );
117
118 $prepared_letter = GetPreparedLetter(
119     (
120         module      => 'test',
121         letter_code => 'TEST_PATRON',
122         tables      => {
123             borrowers => $patron,
124         },
125     )
126 );
127 is( $prepared_letter->{content}, $patron->{borrowernumber}, 'Patron object used correctly with hashref for content' );
128 is( $prepared_letter->{title}, $patron->{firstname}, 'Patron object used correctly with hashref for title' );
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 for content' );
140 is( $prepared_letter->{title}, $patron->{firstname}, 'Patron object used correctly with arrayref for title' );
141
142 $sth->execute( "TEST_BIBLIO", "[% biblio.title %]", "[% biblio.id %]" );
143 $prepared_letter = GetPreparedLetter(
144     (
145         module      => 'test',
146         letter_code => 'TEST_BIBLIO',
147         tables      => {
148             biblio => $item->biblionumber,
149         },
150     )
151 );
152 is( $prepared_letter->{content}, $item->biblionumber, 'Biblio object used correctly for content' );
153 is( $prepared_letter->{title}, $item->biblio->title, 'Biblio object used correctly for title' );
154
155 $sth->execute( "TEST_LIBRARY", "[% branch.branchcode %]", "[% branch.id %]" );
156 $prepared_letter = GetPreparedLetter(
157     (
158         module      => 'test',
159         letter_code => 'TEST_LIBRARY',
160         tables      => {
161             branches => $library->{branchcode}
162         },
163     )
164 );
165 is( $prepared_letter->{content}, $library->{branchcode}, 'Library object used correctly for content' );
166 is( $prepared_letter->{title}, $library->{branchcode}, 'Library object used correctly for title' );
167
168 $sth->execute( "TEST_ITEM", "[% item.barcode %]", "[% item.id %]" );
169 $prepared_letter = GetPreparedLetter(
170     (
171         module      => 'test',
172         letter_code => 'TEST_ITEM',
173         tables      => {
174             items => $item->id()
175         },
176     )
177 );
178 is( $prepared_letter->{content}, $item->id(), 'Item object used correctly for content' );
179 is( $prepared_letter->{title}, $item->barcode, 'Item object used correctly for title' );
180
181 $sth->execute( "TEST_NEWS", "[% news.id %]", "[% news.id %]" );
182 $prepared_letter = GetPreparedLetter(
183     (
184         module      => 'test',
185         letter_code => 'TEST_NEWS',
186         tables      => {
187             opac_news => $news->id()
188         },
189     )
190 );
191 is( $prepared_letter->{content}, $news->id(), 'News object used correctly for content' );
192 is( $prepared_letter->{title}, $news->id(), 'News object used correctly for title' );
193
194 $sth->execute( "TEST_HOLD", "[% hold.borrowernumber %]", "[% hold.id %]" );
195 $prepared_letter = GetPreparedLetter(
196     (
197         module      => 'test',
198         letter_code => 'TEST_HOLD',
199         tables      => {
200             reserves => { borrowernumber => $patron->{borrowernumber}, biblionumber => $item->biblionumber },
201         },
202     )
203 );
204 is( $prepared_letter->{content}, $hold->id(), 'Hold object used correctly for content' );
205 is( $prepared_letter->{title}, $hold->borrowernumber, 'Hold object used correctly for title' );
206
207 eval {
208     $prepared_letter = GetPreparedLetter(
209         (
210             module      => 'test',
211             letter_code => 'TEST_HOLD',
212             tables      => {
213                 reserves => [ $patron->{borrowernumber}, $item->biblionumber ],
214             },
215         )
216     )
217 };
218 my $croak = $@;
219 like( $croak, qr{^Multiple foreign keys \(table reserves\) should be passed using an hashref.*}, "GetPreparedLetter should not be called with arrayref for multiple FK" );
220
221 # Bug 16942
222 $prepared_letter = GetPreparedLetter(
223     (
224         module      => 'test',
225         letter_code => 'TEST_HOLD',
226         tables      => {
227             'branches'    => $library,
228             'borrowers'   => $patron,
229             'biblio'      => $item->biblionumber,
230             'biblioitems' => $item->biblioitemnumber,
231             'reserves'    => $hold->unblessed,
232             'items'       => $hold->itemnumber,
233         }
234     )
235 );
236 is( $prepared_letter->{content}, $hold->id(), 'Hold object used correctly' );
237
238 $sth->execute( "TEST_SERIAL", "[% serial.id %]", "[% serial.id %]" );
239 $prepared_letter = GetPreparedLetter(
240     (
241         module      => 'test',
242         letter_code => 'TEST_SERIAL',
243         tables      => {
244             serial => $serial->id()
245         },
246     )
247 );
248 is( $prepared_letter->{content}, $serial->id(), 'Serial object used correctly' );
249
250 $sth->execute( "TEST_SUBSCRIPTION", "[% subscription.id %]", "[% subscription.id %]" );
251 $prepared_letter = GetPreparedLetter(
252     (
253         module      => 'test',
254         letter_code => 'TEST_SUBSCRIPTION',
255         tables      => {
256             subscription => $subscription->id()
257         },
258     )
259 );
260 is( $prepared_letter->{content}, $subscription->id(), 'Subscription object used correctly' );
261
262 $sth->execute( "TEST_SUGGESTION", "[% suggestion.id %]", "[% suggestion.id %]" );
263 $prepared_letter = GetPreparedLetter(
264     (
265         module      => 'test',
266         letter_code => 'TEST_SUGGESTION',
267         tables      => {
268             suggestions => $suggestion->id()
269         },
270     )
271 );
272 is( $prepared_letter->{content}, $suggestion->id(), 'Suggestion object used correctly' );
273
274 $sth->execute( "TEST_ISSUE", "[% checkout.id %]", "[% checkout.id %]" );
275 $prepared_letter = GetPreparedLetter(
276     (
277         module      => 'test',
278         letter_code => 'TEST_ISSUE',
279         tables      => {
280             issues => $item->id()
281         },
282     )
283 );
284 is( $prepared_letter->{content}, $checkout->id(), 'Checkout object used correctly' );
285
286 $sth->execute( "TEST_MODIFICATION", "[% patron_modification.id %]", "[% patron_modification.id %]" );
287 $prepared_letter = GetPreparedLetter(
288     (
289         module      => 'test',
290         letter_code => 'TEST_MODIFICATION',
291         tables      => {
292             borrower_modifications => $modification->verification_token,
293         },
294     )
295 );
296 is( $prepared_letter->{content}, $modification->id(), 'Patron modification object used correctly' );
297
298 subtest 'regression tests' => sub {
299     plan tests => 8;
300
301     my $library = $builder->build( { source => 'Branch' } );
302     my $patron  = $builder->build( { source => 'Borrower' } );
303     my $item1 = $builder->build_sample_item(
304         {
305             barcode        => 'a_t_barcode',
306             library        => $library->{branchcode},
307             itype          => 'BK',
308             itemcallnumber => 'itemcallnumber1',
309         }
310     );
311     my $biblio1 = $item1->biblio->unblessed;
312     $item1 = $item1->unblessed;
313     my $item2   = $builder->build_sample_item(
314         {
315             barcode        => 'another_t_barcode',
316             library        => $library->{branchcode},
317             itype          => 'BK',
318             itemcallnumber => 'itemcallnumber2',
319         }
320     );
321     my $biblio2 = $item2->biblio->unblessed;
322     $item2 = $item2->unblessed;
323     my $item3   = $builder->build_sample_item(
324         {
325             barcode        => 'another_t_barcode_3',
326             library        => $library->{branchcode},
327             itype          => 'BK',
328             itemcallnumber => 'itemcallnumber3',
329         }
330     );
331     my $biblio3 = $item3->biblio->unblessed;
332     $item3 = $item3->unblessed;
333
334     t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
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         my $reserve_id1 = C4::Reserves::AddReserve(
525             {
526                 branchcode     => $library->{branchcode},
527                 borrowernumber => $patron->{borrowernumber},
528                 biblionumber   => $biblio1->{biblionumber},
529                 notes          => "a note",
530                 itemnumber     => $item1->{itemnumber},
531             }
532         );
533         my $reserve_id2 = C4::Reserves::AddReserve(
534             {
535                 branchcode     => $library->{branchcode},
536                 borrowernumber => $patron->{borrowernumber},
537                 biblionumber   => $biblio1->{biblionumber},
538                 notes          => "a note",
539                 itemnumber     => $item1->{itemnumber},
540             }
541         );
542         my $reserve_id3 = C4::Reserves::AddReserve(
543             {
544                 branchcode     => $library->{branchcode},
545                 borrowernumber => $patron->{borrowernumber},
546                 biblionumber   => $biblio2->{biblionumber},
547                 notes          => "another note",
548                 itemnumber     => $item2->{itemnumber},
549             }
550         );
551
552         my $template = <<EOF;
553 <h5>Date: <<today>></h5>
554
555 <h3> Transfer to/Hold in <<branches.branchname>></h3>
556
557 <h3><<borrowers.surname>>, <<borrowers.firstname>></h3>
558
559 <ul>
560     <li><<borrowers.cardnumber>></li>
561     <li><<borrowers.phone>></li>
562     <li> <<borrowers.address>><br />
563          <<borrowers.address2>><br />
564          <<borrowers.city>>  <<borrowers.zipcode>>
565     </li>
566     <li><<borrowers.email>></li>
567 </ul>
568 <br />
569 <h3>ITEM ON HOLD</h3>
570 <h4><<biblio.title>></h4>
571 <h5><<biblio.author>></h5>
572 <ul>
573    <li><<items.barcode>></li>
574    <li><<items.itemcallnumber>></li>
575    <li><<reserves.waitingdate>></li>
576 </ul>
577 <p>Notes:
578 <pre><<reserves.reserve_id>>=<<reserves.reservenotes>></pre>
579 </p>
580 EOF
581
582         reset_template( { template => $template, code => $code, module => 'circulation' } );
583         my $letter_for_item1 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, reserve_id => $reserve_id1 } );
584         my $letter_for_item2 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, reserve_id => $reserve_id3 } );
585
586         my $tt_template = <<EOF;
587 <h5>Date: [% today | \$KohaDates with_hours => 1 %]</h5>
588
589 <h3> Transfer to/Hold in [% branch.branchname %]</h3>
590
591 <h3>[% borrower.surname %], [% borrower.firstname %]</h3>
592
593 <ul>
594     <li>[% borrower.cardnumber %]</li>
595     <li>[% borrower.phone %]</li>
596     <li> [% borrower.address %]<br />
597          [% borrower.address2 %]<br />
598          [% borrower.city %]  [% borrower.zipcode %]
599     </li>
600     <li>[% borrower.email %]</li>
601 </ul>
602 <br />
603 <h3>ITEM ON HOLD</h3>
604 <h4>[% biblio.title %]</h4>
605 <h5>[% biblio.author %]</h5>
606 <ul>
607    <li>[% item.barcode %]</li>
608    <li>[% item.itemcallnumber %]</li>
609    <li>[% hold.waitingdate | \$KohaDates %]</li>
610 </ul>
611 <p>Notes:
612 <pre>[% hold.reserve_id %]=[% hold.reservenotes %]</pre>
613 </p>
614 EOF
615
616         reset_template( { template => $tt_template, code => $code, module => 'circulation' } );
617         my $tt_letter_for_item1 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, reserve_id => $reserve_id1 } );
618         my $tt_letter_for_item2 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, reserve_id => $reserve_id3 } );
619
620         is( $tt_letter_for_item1->{content}, $letter_for_item1->{content}, );
621         is( $tt_letter_for_item2->{content}, $letter_for_item2->{content}, );
622     };
623
624     subtest 'ISSUESLIP|checkedout|repeat' => sub {
625         plan tests => 2;
626
627         my $code = 'ISSUESLIP';
628         my $now = dt_from_string;
629         my $one_minute_ago = dt_from_string->subtract( minutes => 1 );
630
631         my $branchcode = $library->{branchcode};
632
633         Koha::News->delete;
634         my $news_item = Koha::NewsItem->new({ branchcode => $branchcode, title => "A wonderful news", content => "This is the wonderful news." })->store;
635
636         # historic syntax
637         my $template = <<EOF;
638 <h3><<branches.branchname>></h3>
639 Checked out to <<borrowers.title>> <<borrowers.firstname>> <<borrowers.initials>> <<borrowers.surname>> <br />
640 (<<borrowers.cardnumber>>) <br />
641
642 <<today>><br />
643
644 <h4>Checked out</h4>
645 <checkedout>
646 <p>
647 <<biblio.title>> <br />
648 Barcode: <<items.barcode>><br />
649 Date due: <<issues.date_due | dateonly>><br />
650 </p>
651 </checkedout>
652
653 <h4>Overdues</h4>
654 <overdue>
655 <p>
656 <<biblio.title>> <br />
657 Barcode: <<items.barcode>><br />
658 Date due: <<issues.date_due | dateonly>><br />
659 </p>
660 </overdue>
661
662 <hr>
663
664 <h4 style="text-align: center; font-style:italic;">News</h4>
665 <news>
666 <div class="newsitem">
667 <h5 style="margin-bottom: 1px; margin-top: 1px"><b><<opac_news.title>></b></h5>
668 <p style="margin-bottom: 1px; margin-top: 1px"><<opac_news.content>></p>
669 <p class="newsfooter" style="font-size: 8pt; font-style:italic; margin-bottom: 1px; margin-top: 1px">Posted on <<opac_news.published_on>></p>
670 <hr />
671 </div>
672 </news>
673 EOF
674
675         reset_template( { template => $template, code => $code, module => 'circulation' } );
676
677         my $checkout = C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
678         $checkout->set( { timestamp => $now, issuedate => $one_minute_ago } )->store;
679         my $first_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
680
681         $checkout = C4::Circulation::AddIssue( $patron, $item2->{barcode} ); # Add a second checkout
682         $checkout->set( { timestamp => $now, issuedate => $now } )->store;
683         my $yesterday = dt_from_string->subtract( days => 1 );
684         C4::Circulation::AddIssue( $patron, $item3->{barcode}, $yesterday ); # Add an overdue
685         my $second_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
686
687         # Cleanup
688         AddReturn( $item1->{barcode} );
689         AddReturn( $item2->{barcode} );
690         AddReturn( $item3->{barcode} );
691
692         # TT syntax
693         my $tt_template = <<EOF;
694 <h3>[% branch.branchname %]</h3>
695 Checked out to [% borrower.title %] [% borrower.firstname %] [% borrower.initials %] [% borrower.surname %] <br />
696 ([% borrower.cardnumber %]) <br />
697
698 [% today | \$KohaDates with_hours => 1 %]<br />
699
700 <h4>Checked out</h4>
701 [% FOREACH checkout IN checkouts %]
702 [%~ SET item = checkout.item %]
703 [%~ SET biblio = checkout.item.biblio %]
704 <p>
705 [% biblio.title %] <br />
706 Barcode: [% item.barcode %]<br />
707 Date due: [% checkout.date_due | \$KohaDates %]<br />
708 </p>
709 [% END %]
710
711 <h4>Overdues</h4>
712 [% FOREACH overdue IN overdues %]
713 [%~ SET item = overdue.item %]
714 [%~ SET biblio = overdue.item.biblio %]
715 <p>
716 [% biblio.title %] <br />
717 Barcode: [% item.barcode %]<br />
718 Date due: [% overdue.date_due | \$KohaDates %]<br />
719 </p>
720 [% END %]
721
722 <hr>
723
724 <h4 style="text-align: center; font-style:italic;">News</h4>
725 [% FOREACH n IN news %]
726 <div class="newsitem">
727 <h5 style="margin-bottom: 1px; margin-top: 1px"><b>[% n.title %]</b></h5>
728 <p style="margin-bottom: 1px; margin-top: 1px">[% n.content %]</p>
729 <p class="newsfooter" style="font-size: 8pt; font-style:italic; margin-bottom: 1px; margin-top: 1px">Posted on [% n.timestamp | \$KohaDates %]</p>
730 <hr />
731 </div>
732 [% END %]
733 EOF
734
735         reset_template( { template => $tt_template, code => $code, module => 'circulation' } );
736
737         $checkout = C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
738         $checkout->set( { timestamp => $now, issuedate => $one_minute_ago } )->store;
739         my $first_tt_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
740
741         $checkout = C4::Circulation::AddIssue( $patron, $item2->{barcode} ); # Add a second checkout
742         $checkout->set( { timestamp => $now, issuedate => $now } )->store;
743         C4::Circulation::AddIssue( $patron, $item3->{barcode}, $yesterday ); # Add an overdue
744         my $second_tt_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
745
746         # There is too many line breaks generated by the historic syntax
747         $second_slip->{content} =~ s|</p>\n\n\n<p>|</p>\n\n<p>|s;
748
749         is( $first_tt_slip->{content}, $first_slip->{content}, );
750         is( $second_tt_slip->{content}, $second_slip->{content}, );
751
752         # Cleanup
753         AddReturn( $item1->{barcode} );
754         AddReturn( $item2->{barcode} );
755         AddReturn( $item3->{barcode} );
756     };
757
758     subtest 'ODUE|items.content|item' => sub {
759         plan tests => 1;
760
761         my $code = 'ODUE';
762
763         my $branchcode = $library->{branchcode};
764
765         # historic syntax
766         # FIXME items.fine does not work with TT notices
767         # See bug 17976
768         # <item> should contain Fine: <<items.fine>></item>
769         my $template = <<EOF;
770 Dear <<borrowers.firstname>> <<borrowers.surname>>,
771
772 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.
773
774 <<branches.branchname>>
775 <<branches.branchaddress1>>
776 <<branches.branchaddress2>> <<branches.branchaddress3>>
777 Phone: <<branches.branchphone>>
778 Fax: <<branches.branchfax>>
779 Email: <<branches.branchemail>>
780
781 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.
782
783 The following item(s) is/are currently overdue:
784
785 <item>"<<biblio.title>>" by <<biblio.author>>, <<items.itemcallnumber>>, Barcode: <<items.barcode>></item>
786
787 <<items.content>>
788
789 Thank-you for your prompt attention to this matter.
790
791 <<branches.branchname>> Staff
792 EOF
793
794         reset_template( { template => $template, code => $code, module => 'circulation' } );
795
796         my $yesterday = dt_from_string->subtract( days => 1 );
797         my $two_days_ago = dt_from_string->subtract( days => 2 );
798         my $issue1 = C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
799         my $issue2 = C4::Circulation::AddIssue( $patron, $item2->{barcode}, $yesterday ); # Add an first overdue
800         my $issue3 = C4::Circulation::AddIssue( $patron, $item3->{barcode}, $two_days_ago ); # Add an second overdue
801         $issue1 = $issue1->unblessed;
802         $issue2 = $issue2->unblessed;
803         $issue3 = $issue3->unblessed;
804
805         # For items.content
806         my @item_fields = qw( date_due title barcode author itemnumber );
807         my $items_content = C4::Letters::get_item_content( { item => { %$item1, %$biblio1, %$issue1 }, item_content_fields => \@item_fields, dateonly => 1 } );
808           $items_content .= C4::Letters::get_item_content( { item => { %$item2, %$biblio2, %$issue2 }, item_content_fields => \@item_fields, dateonly => 1 } );
809           $items_content .= C4::Letters::get_item_content( { item => { %$item3, %$biblio3, %$issue3 }, item_content_fields => \@item_fields, dateonly => 1 } );
810
811         my @items = ( $item1, $item2, $item3 );
812         my $letter = C4::Overdues::parse_overdues_letter(
813             {
814                 letter_code => $code,
815                 borrowernumber => $patron->{borrowernumber},
816                 branchcode  => $library->{branchcode},
817                 items       => \@items,
818                 substitute  => {
819                     bib                    => $library->{branchname},
820                     'items.content'        => $items_content,
821                     count                  => scalar( @items ),
822                     message_transport_type => 'email',
823                 }
824             }
825         );
826
827         # Cleanup
828         AddReturn( $item1->{barcode} );
829         AddReturn( $item2->{barcode} );
830         AddReturn( $item3->{barcode} );
831
832
833         # historic syntax
834         my $tt_template = <<EOF;
835 Dear [% borrower.firstname %] [% borrower.surname %],
836
837 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.
838
839 [% branch.branchname %]
840 [% branch.branchaddress1 %]
841 [% branch.branchaddress2 %] [% branch.branchaddress3 %]
842 Phone: [% branch.branchphone %]
843 Fax: [% branch.branchfax %]
844 Email: [% branch.branchemail %]
845
846 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.
847
848 The following item(s) is/are currently overdue:
849
850 [% FOREACH overdue IN overdues %]
851 [%~ SET item = overdue.item ~%]
852 "[% item.biblio.title %]" by [% item.biblio.author %], [% item.itemcallnumber %], Barcode: [% item.barcode %]
853 [% END %]
854 [% FOREACH overdue IN overdues %]
855 [%~ SET item = overdue.item ~%]
856 [% overdue.date_due | \$KohaDates %]\t[% item.biblio.title %]\t[% item.barcode %]\t[% item.biblio.author %]\t[% item.itemnumber %]
857 [% END %]
858
859 Thank-you for your prompt attention to this matter.
860
861 [% branch.branchname %] Staff
862 EOF
863
864         reset_template( { template => $tt_template, code => $code, module => 'circulation' } );
865
866         C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
867         C4::Circulation::AddIssue( $patron, $item2->{barcode}, $yesterday ); # Add an first overdue
868         C4::Circulation::AddIssue( $patron, $item3->{barcode}, $two_days_ago ); # Add an second overdue
869
870         my $tt_letter = C4::Overdues::parse_overdues_letter(
871             {
872                 letter_code => $code,
873                 borrowernumber => $patron->{borrowernumber},
874                 branchcode  => $library->{branchcode},
875                 items       => \@items,
876                 substitute  => {
877                     bib                    => $library->{branchname},
878                     'items.content'        => $items_content,
879                     count                  => scalar( @items ),
880                     message_transport_type => 'email',
881                 }
882             }
883         );
884
885         is( $tt_letter->{content}, $letter->{content}, );
886     };
887
888     subtest 'Bug 19743 - Header and Footer should be updated on each item for checkin / checkout / renewal notices' => sub {
889         plan tests => 8;
890
891         my $checkout_code = 'CHECKOUT';
892         my $checkin_code = 'CHECKIN';
893
894         my $dbh = C4::Context->dbh;
895         $dbh->do("DELETE FROM letter");
896         $dbh->do("DELETE FROM issues");
897         $dbh->do("DELETE FROM message_queue");
898
899         # Enable notification for CHECKOUT - Things are hardcoded here but should work with default data
900         $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 6 );
901         my $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
902         $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
903         # Enable notification for CHECKIN - Things are hardcoded here but should work with default data
904         $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 5 );
905         $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
906         $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
907
908         my $checkout_template = q|
909 <<branches.branchname>>
910 ----
911 ----
912 |;
913         reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
914         my $checkin_template = q[
915 <<branches.branchname>>
916 ----
917 ----
918 ];
919         reset_template( { template => $checkin_template, code => $checkin_code, module => 'circulation' } );
920
921         my $issue = C4::Circulation::AddIssue( $patron, $item1->{barcode} );
922         my $first_checkout_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
923
924         my $library_object = Koha::Libraries->find( $issue->branchcode );
925         my $old_branchname = $library_object->branchname;
926         my $new_branchname = "Kyle M Hall Memorial Library";
927
928         # Change branch name for second checkout notice
929         $library_object->branchname($new_branchname);
930         $library_object->store();
931
932         C4::Circulation::AddIssue( $patron, $item2->{barcode} );
933         my $second_checkout_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
934
935         # Restore old name for first checkin notice
936         $library_object->branchname( $old_branchname );
937         $library_object->store();
938
939         AddReturn( $item1->{barcode} );
940         my $first_checkin_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
941
942         # Change branch name for second checkin notice
943         $library_object->branchname($new_branchname);
944         $library_object->store();
945
946         AddReturn( $item2->{barcode} );
947         my $second_checkin_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
948
949         # Restore old name for first TT checkout notice
950         $library_object->branchname( $old_branchname );
951         $library_object->store();
952
953         Koha::Notice::Messages->delete;
954
955         # TT syntax
956         $checkout_template = q|
957 [% branch.branchname %]
958 ----
959 ----
960 |;
961         reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
962         $checkin_template = q[
963 [% branch.branchname %]
964 ----
965 ----
966 ];
967         reset_template( { template => $checkin_template, code => $checkin_code, module => 'circulation' } );
968
969         C4::Circulation::AddIssue( $patron, $item1->{barcode} );
970         my $first_checkout_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
971
972         # Change branch name for second checkout notice
973         $library_object->branchname($new_branchname);
974         $library_object->store();
975
976         C4::Circulation::AddIssue( $patron, $item2->{barcode} );
977         my $second_checkout_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
978
979         # Restore old name for first checkin notice
980         $library_object->branchname( $old_branchname );
981         $library_object->store();
982
983         AddReturn( $item1->{barcode} );
984         my $first_checkin_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
985 #
986         # Change branch name for second checkin notice
987         $library_object->branchname($new_branchname);
988         $library_object->store();
989
990         AddReturn( $item2->{barcode} );
991         my $second_checkin_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
992
993         my $first_letter = qq[
994 $old_branchname
995 ];
996         my $second_letter = qq[
997 $new_branchname
998 ];
999
1000
1001         is( $first_checkout_letter->content, $first_letter, 'Verify first checkout letter' );
1002         is( $second_checkout_letter->content, $second_letter, 'Verify second checkout letter' );
1003         is( $first_checkin_letter->content, $first_letter, 'Verify first checkin letter'  );
1004         is( $second_checkin_letter->content, $second_letter, 'Verify second checkin letter' );
1005
1006         is( $first_checkout_tt_letter->content, $first_letter, 'Verify TT first checkout letter' );
1007         is( $second_checkout_tt_letter->content, $second_letter, 'Verify TT second checkout letter' );
1008         is( $first_checkin_tt_letter->content, $first_letter, 'Verify TT first checkin letter'  );
1009         is( $second_checkin_tt_letter->content, $second_letter, 'Verify TT second checkin letter' );
1010     };
1011
1012 };
1013
1014 subtest 'loops' => sub {
1015     plan tests => 2;
1016     my $code = "TEST";
1017     my $module = "TEST";
1018
1019     subtest 'primary key is AI' => sub {
1020         plan tests => 1;
1021         my $patron_1 = $builder->build({ source => 'Borrower' });
1022         my $patron_2 = $builder->build({ source => 'Borrower' });
1023
1024         my $template = q|[% FOREACH patron IN borrowers %][% patron.surname %][% END %]|;
1025         reset_template( { template => $template, code => $code, module => $module } );
1026         my $letter = GetPreparedLetter( module => $module, letter_code => $code, loops => { borrowers => [ $patron_1->{borrowernumber}, $patron_2->{borrowernumber} ] } );
1027         my $expected_letter = join '', ( $patron_1->{surname}, $patron_2->{surname} );
1028         is( $letter->{content}, $expected_letter, );
1029     };
1030
1031     subtest 'foreign key is used' => sub {
1032         plan tests => 1;
1033         my $patron_1 = $builder->build({ source => 'Borrower' });
1034         my $patron_2 = $builder->build({ source => 'Borrower' });
1035         my $checkout_1 = $builder->build({ source => 'Issue', value => { borrowernumber => $patron_1->{borrowernumber} } } );
1036         my $checkout_2 = $builder->build({ source => 'Issue', value => { borrowernumber => $patron_1->{borrowernumber} } } );
1037
1038         my $template = q|[% FOREACH checkout IN checkouts %][% checkout.issue_id %][% END %]|;
1039         reset_template( { template => $template, code => $code, module => $module } );
1040         my $letter = GetPreparedLetter( module => $module, letter_code => $code, loops => { issues => [ $checkout_1->{itemnumber}, $checkout_2->{itemnumber} ] } );
1041         my $expected_letter = join '', ( $checkout_1->{issue_id}, $checkout_2->{issue_id} );
1042         is( $letter->{content}, $expected_letter, );
1043     };
1044 };
1045
1046 subtest 'add_tt_filters' => sub {
1047     plan tests => 1;
1048     my $code   = "TEST";
1049     my $module = "TEST";
1050
1051     my $patron = $builder->build_object(
1052         {
1053             class => 'Koha::Patrons',
1054             value => { surname => "with_punctuation_" }
1055         }
1056     );
1057     my $biblio = $builder->build_object(
1058         { class => 'Koha::Biblios', value => { title => "with_punctuation_" } }
1059     );
1060     my $biblioitem = $builder->build_object(
1061         {
1062             class => 'Koha::Biblioitems',
1063             value => {
1064                 biblionumber => $biblio->biblionumber,
1065                 isbn         => "with_punctuation_"
1066             }
1067         }
1068     );
1069
1070     my $template = q|patron=[% borrower.surname %];biblio=[% biblio.title %];biblioitems=[% biblioitem.isbn %]|;
1071     reset_template( { template => $template, code => $code, module => $module } );
1072     my $letter = GetPreparedLetter(
1073         module      => $module,
1074         letter_code => $code,
1075         tables      => {
1076             borrowers   => $patron->borrowernumber,
1077             biblio      => $biblio->biblionumber,
1078             biblioitems => $biblioitem->biblioitemnumber
1079         }
1080     );
1081     my $expected_letter = q|patron=with_punctuation_;biblio=with_punctuation;biblioitems=with_punctuation|;
1082     is( $letter->{content}, $expected_letter, "Pre-processing should call TT plugin to remove punctuation if table is biblio or biblioitems");
1083 };
1084
1085 subtest 'Handle includes' => sub {
1086     plan tests => 1;
1087     my $cgi = CGI->new();
1088     my $code = 'TEST_INCLUDE';
1089     my $account =
1090       $builder->build_object( { class => 'Koha::Account::Lines', value => { credit_type_code => 'PAYMENT', status => 'CANCELLED' } } );
1091     my $template = <<EOF;
1092 [%- USE Price -%]
1093 [%- PROCESS 'accounts.inc' -%]
1094 [%- PROCESS account_type_description account=credit -%]
1095 EOF
1096     reset_template({ template => $template, code => $code, module => 'test' });
1097     my $letter = GetPreparedLetter(
1098         module      => 'test',
1099         letter_code => $code,
1100         tables      => {
1101             credits => $account->accountlines_id
1102         }
1103     );
1104     is($letter->{content},'    <span>Payment<span> (Cancelled)</span>    </span>', "Include used in notice");
1105 };
1106
1107 subtest 'Dates formatting' => sub {
1108     plan tests => 1;
1109     my $code = 'TEST_DATE';
1110     t::lib::Mocks::mock_preference('dateformat', 'metric'); # MM/DD/YYYY
1111     my $biblio = $builder->build_object(
1112         {
1113             class => 'Koha::Biblios',
1114             value => {
1115                 timestamp   => '2018-12-13 20:21:22',
1116                 datecreated => '2018-12-13'
1117             }
1118         }
1119     );
1120     my $template = <<EOF;
1121 [%- USE KohaDates -%]
1122 [% biblio.timestamp %]
1123 [% biblio.timestamp | \$KohaDates %]
1124 [% biblio.timestamp | \$KohaDates with_hours => 1 %]
1125
1126 [% biblio.datecreated %]
1127 [% biblio.datecreated | \$KohaDates %]
1128 [% biblio.datecreated | \$KohaDates with_hours => 1 %]
1129
1130 [% biblio.timestamp | \$KohaDates dateformat => 'iso' %]
1131 [% KohaDates.output_preference( str => biblio.timestamp, dateformat => 'iso' ) %]
1132 [% KohaDates.output_preference( str => biblio.timestamp, dateformat => 'iso', dateonly => 1 ) %]
1133 EOF
1134     reset_template({ template => $template, code => $code, module => 'test' });
1135     my $letter = GetPreparedLetter(
1136         module => 'test',
1137         letter_code => $code,
1138         tables => {
1139             biblio => $biblio->biblionumber,
1140         }
1141     );
1142     my $expected_content = sprintf("%s\n%s\n%s\n\n%s\n%s\n%s\n\n%s\n%s\n%s\n",
1143         '2018-12-13 20:21:22',
1144         '13/12/2018',
1145         '13/12/2018 20:21',
1146
1147         '2018-12-13',
1148         '13/12/2018',
1149         '13/12/2018 00:00',
1150
1151         '2018-12-13',
1152         '2018-12-13 20:21',
1153         '2018-12-13',
1154     );
1155     is( $letter->{content}, $expected_content );
1156 };
1157
1158 sub reset_template {
1159     my ( $params ) = @_;
1160     my $template   = $params->{template};
1161     my $code       = $params->{code};
1162     my $module     = $params->{module} || 'test_module';
1163
1164     Koha::Notice::Templates->search( { code => $code } )->delete;
1165     Koha::Notice::Template->new(
1166         {
1167             module                 => $module,
1168             code                   => $code,
1169             branchcode             => '',
1170             name                   => $code,
1171             title                  => $code,
1172             message_transport_type => 'email',
1173             content                => $template
1174         }
1175     )->store;
1176 }
1177
1178 sub process_letter {
1179     my ($params)   = @_;
1180     my $template   = $params->{template};
1181     my $tables     = $params->{tables};
1182     my $substitute = $params->{substitute};
1183     my $code       = $params->{code};
1184     my $module     = $params->{module} || 'test_module';
1185     my $branchcode = $params->{branchcode};
1186
1187     reset_template( $params );
1188
1189     my $letter = C4::Letters::GetPreparedLetter(
1190         module      => $module,
1191         letter_code => $code,
1192         branchcode  => '',
1193         tables      => $tables,
1194         substitute  => $substitute,
1195     );
1196     return $letter;
1197 }
1198
1199 $schema->storage->txn_rollback;