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