Bug 17600: Standardize our EXPORT_OK
[srvgit] / t / db_dependent / Letters / TemplateToolkit.t
old mode 100644 (file)
new mode 100755 (executable)
index b83c2b6..7e72517
 # along with Koha; if not, see <http://www.gnu.org/licenses>.
 
 use Modern::Perl;
-use Test::More tests => 17;
+use Test::More tests => 28;
+use Test::MockModule;
 use Test::Warn;
 
 use MARC::Record;
 
 use t::lib::TestBuilder;
+use t::lib::Mocks;
 
-use C4::Circulation;
-use C4::Letters;
-use C4::Members;
+use C4::Circulation qw( AddIssue AddReturn );
+use C4::Letters qw( GetPreparedLetter );
+use C4::Members qw( IssueSlip );
 use C4::Biblio;
 use Koha::Database;
 use Koha::DateUtils;
@@ -52,55 +54,55 @@ $schema->storage->txn_begin();
 my $builder = t::lib::TestBuilder->new();
 
 my $dbh = C4::Context->dbh;
-$dbh->{RaiseError} = 1;
 
 $dbh->do(q|DELETE FROM letter|);
 
-my $date = dt_from_string;
+my $now_value       = dt_from_string();
+my $mocked_datetime = Test::MockModule->new('DateTime');
+$mocked_datetime->mock( 'now', sub { return $now_value->clone; } );
 
 my $library = $builder->build( { source => 'Branch' } );
 my $patron  = $builder->build( { source => 'Borrower' } );
 my $patron2 = $builder->build( { source => 'Borrower' } );
 
-my $biblio = Koha::Biblio->new(
+my $item = $builder->build_sample_item();
+my $hold = $builder->build_object(
     {
-        title => 'Test Biblio'
-    }
-)->store();
-
-my $biblioitem = Koha::Biblioitem->new(
-    {
-        biblionumber => $biblio->id()
+        class => 'Koha::Holds',
+        value => {
+            borrowernumber => $patron->{borrowernumber},
+            biblionumber   => $item->biblionumber
+        }
     }
-)->store();
+);
 
-my $item = Koha::Item->new(
+my $news = $builder->build_object(
     {
-        biblionumber     => $biblio->id(),
-        biblioitemnumber => $biblioitem->id()
+        class => 'Koha::News',
+        value => { title => 'a news title', content => 'a news content' }
     }
-)->store();
-
-my $hold = Koha::Hold->new(
+);
+my $serial       = $builder->build_object( { class => 'Koha::Serials' } );
+my $subscription = $builder->build_object( { class => 'Koha::Subscriptions' } );
+my $suggestion   = $builder->build_object( { class => 'Koha::Suggestions' } );
+my $checkout     = $builder->build_object(
+    { class => 'Koha::Checkouts', value => { itemnumber => $item->id } } );
+my $modification = $builder->build_object(
     {
-        borrowernumber => $patron->{borrowernumber},
-        biblionumber   => $biblio->id()
+        class => 'Koha::Patron::Modifications',
+        value => {
+            verification_token => "TEST",
+            changed_fields     => 'firstname,surname'
+        }
     }
-)->store();
-
-my $news         = Koha::NewsItem->new()->store();
-my $serial       = Koha::Serial->new()->store();
-my $subscription = Koha::Subscription->new()->store();
-my $suggestion   = Koha::Suggestion->new()->store();
-my $checkout     = Koha::Checkout->new( { itemnumber => $item->id() } )->store();
-my $modification = Koha::Patron::Modification->new( { verification_token => "TEST" } )->store();
+);
 
 my $prepared_letter;
 
 my $sth =
-  $dbh->prepare(q{INSERT INTO letter (module, code, name, title, content) VALUES ('test',?,'Test','Test',?)});
+  $dbh->prepare(q{INSERT INTO letter (module, code, name, title, content) VALUES ('test',?,'Test',?,?)});
 
-$sth->execute( "TEST_PATRON", "[% borrower.id %]" );
+$sth->execute( "TEST_PATRON", "[% borrower.firstname %]", "[% borrower.id %]" );
 $prepared_letter = GetPreparedLetter(
     (
         module      => 'test',
@@ -110,7 +112,8 @@ $prepared_letter = GetPreparedLetter(
         },
     )
 );
-is( $prepared_letter->{content}, $patron->{borrowernumber}, 'Patron object used correctly with scalar' );
+is( $prepared_letter->{content}, $patron->{borrowernumber}, 'Patron object used correctly with scalar for content' );
+is( $prepared_letter->{title}, $patron->{firstname}, 'Patron object used correctly with scalar for title' );
 
 $prepared_letter = GetPreparedLetter(
     (
@@ -121,7 +124,8 @@ $prepared_letter = GetPreparedLetter(
         },
     )
 );
-is( $prepared_letter->{content}, $patron->{borrowernumber}, 'Patron object used correctly with hashref' );
+is( $prepared_letter->{content}, $patron->{borrowernumber}, 'Patron object used correctly with hashref for content' );
+is( $prepared_letter->{title}, $patron->{firstname}, 'Patron object used correctly with hashref for title' );
 
 $prepared_letter = GetPreparedLetter(
     (
@@ -132,21 +136,23 @@ $prepared_letter = GetPreparedLetter(
         },
     )
 );
-is( $prepared_letter->{content}, $patron->{borrowernumber}, 'Patron object used correctly with arrayref' );
+is( $prepared_letter->{content}, $patron->{borrowernumber}, 'Patron object used correctly with arrayref for content' );
+is( $prepared_letter->{title}, $patron->{firstname}, 'Patron object used correctly with arrayref for title' );
 
-$sth->execute( "TEST_BIBLIO", "[% biblio.id %]" );
+$sth->execute( "TEST_BIBLIO", "[% biblio.title %]", "[% biblio.id %]" );
 $prepared_letter = GetPreparedLetter(
     (
         module      => 'test',
         letter_code => 'TEST_BIBLIO',
         tables      => {
-            biblio => $biblio->id(),
+            biblio => $item->biblionumber,
         },
     )
 );
-is( $prepared_letter->{content}, $biblio->id, 'Biblio object used correctly' );
+is( $prepared_letter->{content}, $item->biblionumber, 'Biblio object used correctly for content' );
+is( $prepared_letter->{title}, $item->biblio->title, 'Biblio object used correctly for title' );
 
-$sth->execute( "TEST_LIBRARY", "[% branch.id %]" );
+$sth->execute( "TEST_LIBRARY", "[% branch.branchcode %]", "[% branch.id %]" );
 $prepared_letter = GetPreparedLetter(
     (
         module      => 'test',
@@ -156,9 +162,10 @@ $prepared_letter = GetPreparedLetter(
         },
     )
 );
-is( $prepared_letter->{content}, $library->{branchcode}, 'Library object used correctly' );
+is( $prepared_letter->{content}, $library->{branchcode}, 'Library object used correctly for content' );
+is( $prepared_letter->{title}, $library->{branchcode}, 'Library object used correctly for title' );
 
-$sth->execute( "TEST_ITEM", "[% item.id %]" );
+$sth->execute( "TEST_ITEM", "[% item.barcode %]", "[% item.id %]" );
 $prepared_letter = GetPreparedLetter(
     (
         module      => 'test',
@@ -168,9 +175,10 @@ $prepared_letter = GetPreparedLetter(
         },
     )
 );
-is( $prepared_letter->{content}, $item->id(), 'Item object used correctly' );
+is( $prepared_letter->{content}, $item->id(), 'Item object used correctly for content' );
+is( $prepared_letter->{title}, $item->barcode, 'Item object used correctly for title' );
 
-$sth->execute( "TEST_NEWS", "[% news.id %]" );
+$sth->execute( "TEST_NEWS", "[% news.id %]", "[% news.id %]" );
 $prepared_letter = GetPreparedLetter(
     (
         module      => 'test',
@@ -180,19 +188,21 @@ $prepared_letter = GetPreparedLetter(
         },
     )
 );
-is( $prepared_letter->{content}, $news->id(), 'News object used correctly' );
+is( $prepared_letter->{content}, $news->id(), 'News object used correctly for content' );
+is( $prepared_letter->{title}, $news->id(), 'News object used correctly for title' );
 
-$sth->execute( "TEST_HOLD", "[% hold.id %]" );
+$sth->execute( "TEST_HOLD", "[% hold.borrowernumber %]", "[% hold.id %]" );
 $prepared_letter = GetPreparedLetter(
     (
         module      => 'test',
         letter_code => 'TEST_HOLD',
         tables      => {
-            reserves => { borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio->id() },
+            reserves => { borrowernumber => $patron->{borrowernumber}, biblionumber => $item->biblionumber },
         },
     )
 );
-is( $prepared_letter->{content}, $hold->id(), 'Hold object used correctly' );
+is( $prepared_letter->{content}, $hold->id(), 'Hold object used correctly for content' );
+is( $prepared_letter->{title}, $hold->borrowernumber, 'Hold object used correctly for title' );
 
 eval {
     $prepared_letter = GetPreparedLetter(
@@ -200,7 +210,7 @@ eval {
             module      => 'test',
             letter_code => 'TEST_HOLD',
             tables      => {
-                reserves => [ $patron->{borrowernumber}, $biblio->id() ],
+                reserves => [ $patron->{borrowernumber}, $item->biblionumber ],
             },
         )
     )
@@ -216,8 +226,8 @@ $prepared_letter = GetPreparedLetter(
         tables      => {
             'branches'    => $library,
             'borrowers'   => $patron,
-            'biblio'      => $biblio->id,
-            'biblioitems' => $biblioitem->id,
+            'biblio'      => $item->biblionumber,
+            'biblioitems' => $item->biblioitemnumber,
             'reserves'    => $hold->unblessed,
             'items'       => $hold->itemnumber,
         }
@@ -225,7 +235,7 @@ $prepared_letter = GetPreparedLetter(
 );
 is( $prepared_letter->{content}, $hold->id(), 'Hold object used correctly' );
 
-$sth->execute( "TEST_SERIAL", "[% serial.id %]" );
+$sth->execute( "TEST_SERIAL", "[% serial.id %]", "[% serial.id %]" );
 $prepared_letter = GetPreparedLetter(
     (
         module      => 'test',
@@ -237,7 +247,7 @@ $prepared_letter = GetPreparedLetter(
 );
 is( $prepared_letter->{content}, $serial->id(), 'Serial object used correctly' );
 
-$sth->execute( "TEST_SUBSCRIPTION", "[% subscription.id %]" );
+$sth->execute( "TEST_SUBSCRIPTION", "[% subscription.id %]", "[% subscription.id %]" );
 $prepared_letter = GetPreparedLetter(
     (
         module      => 'test',
@@ -249,7 +259,7 @@ $prepared_letter = GetPreparedLetter(
 );
 is( $prepared_letter->{content}, $subscription->id(), 'Subscription object used correctly' );
 
-$sth->execute( "TEST_SUGGESTION", "[% suggestion.id %]" );
+$sth->execute( "TEST_SUGGESTION", "[% suggestion.id %]", "[% suggestion.id %]" );
 $prepared_letter = GetPreparedLetter(
     (
         module      => 'test',
@@ -261,7 +271,7 @@ $prepared_letter = GetPreparedLetter(
 );
 is( $prepared_letter->{content}, $suggestion->id(), 'Suggestion object used correctly' );
 
-$sth->execute( "TEST_ISSUE", "[% checkout.id %]" );
+$sth->execute( "TEST_ISSUE", "[% checkout.id %]", "[% checkout.id %]" );
 $prepared_letter = GetPreparedLetter(
     (
         module      => 'test',
@@ -273,7 +283,7 @@ $prepared_letter = GetPreparedLetter(
 );
 is( $prepared_letter->{content}, $checkout->id(), 'Checkout object used correctly' );
 
-$sth->execute( "TEST_MODIFICATION", "[% patron_modification.id %]" );
+$sth->execute( "TEST_MODIFICATION", "[% patron_modification.id %]", "[% patron_modification.id %]" );
 $prepared_letter = GetPreparedLetter(
     (
         module      => 'test',
@@ -286,37 +296,42 @@ $prepared_letter = GetPreparedLetter(
 is( $prepared_letter->{content}, $modification->id(), 'Patron modification object used correctly' );
 
 subtest 'regression tests' => sub {
-    plan tests => 3;
+    plan tests => 8;
 
     my $library = $builder->build( { source => 'Branch' } );
     my $patron  = $builder->build( { source => 'Borrower' } );
-    my $biblio1 = Koha::Biblio->new({title => 'Test Biblio 1'})->store->unblessed;
-    my $biblioitem1 = Koha::Biblioitem->new({biblionumber => $biblio1->{biblionumber}})->store()->unblessed;
-    my $item1 = Koha::Item->new(
+    my $item1 = $builder->build_sample_item(
+        {
+            barcode        => 'a_t_barcode',
+            library        => $library->{branchcode},
+            itype          => 'BK',
+            itemcallnumber => 'itemcallnumber1',
+        }
+    );
+    my $biblio1 = $item1->biblio->unblessed;
+    $item1 = $item1->unblessed;
+    my $item2   = $builder->build_sample_item(
         {
-            biblionumber     => $biblio1->{biblionumber},
-            biblioitemnumber => $biblioitem1->{biblioitemnumber},
-            barcode          => 'a_t_barcode',
-            homebranch       => $library->{branchcode},
-            holdingbranch    => $library->{branchcode},
-            itype            => 'BK',
+            barcode        => 'another_t_barcode',
+            library        => $library->{branchcode},
+            itype          => 'BK',
+            itemcallnumber => 'itemcallnumber2',
         }
-    )->store->unblessed;
-    my $biblio2 = Koha::Biblio->new({title => 'Test Biblio 2'})->store->unblessed;
-    my $biblioitem2 = Koha::Biblioitem->new({biblionumber => $biblio2->{biblionumber}})->store()->unblessed;
-    my $item2 = Koha::Item->new(
+    );
+    my $biblio2 = $item2->biblio->unblessed;
+    $item2 = $item2->unblessed;
+    my $item3   = $builder->build_sample_item(
         {
-            biblionumber     => $biblio2->{biblionumber},
-            biblioitemnumber => $biblioitem2->{biblioitemnumber},
-            barcode          => 'another_t_barcode',
-            homebranch       => $library->{branchcode},
-            holdingbranch    => $library->{branchcode},
-            itype            => 'BK',
+            barcode        => 'another_t_barcode_3',
+            library        => $library->{branchcode},
+            itype          => 'BK',
+            itemcallnumber => 'itemcallnumber3',
         }
-    )->store->unblessed;
+    );
+    my $biblio3 = $item3->biblio->unblessed;
+    $item3 = $item3->unblessed;
 
-    C4::Context->_new_userenv('xxx');
-    C4::Context->set_userenv(0,0,0,'firstname','surname', $library->{branchcode}, 'Midway Public Library', '', '', '');
+    t::lib::Mocks::mock_userenv({ branchcode => $library->{branchcode} });
 
     subtest 'ACQ_NOTIF_ON_RECEIV ' => sub {
         plan tests => 1;
@@ -472,6 +487,528 @@ Thank you for visiting [% branch.branchname %].
         is( $second_checkin_tt_letter->content, $second_checkin_letter->content, 'Verify second checkin letter' );
 
     };
+
+    subtest 'DUEDGST|count' => sub {
+        plan tests => 1;
+
+        my $code = 'DUEDGST';
+
+        my $dbh = C4::Context->dbh;
+        # Enable notification for DUEDGST - Things are hardcoded here but should work with default data
+        $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 1 );
+        my $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
+        $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
+
+        my $params = {
+            code => $code,
+            substitute => { count => 42 },
+        };
+
+        my $template = q|
+You have <<count>> items due
+        |;
+        my $letter = process_letter( { template => $template, %$params });
+
+        my $tt_template = q|
+You have [% count %] items due
+        |;
+        my $tt_letter = process_letter( { template => $tt_template, %$params });
+        is( $tt_letter->{content}, $letter->{content}, );
+    };
+
+    subtest 'HOLD_SLIP|dates|today' => sub {
+        plan tests => 2;
+
+        my $code = 'HOLD_SLIP';
+
+        my $reserve_id1 = C4::Reserves::AddReserve(
+            {
+                branchcode     => $library->{branchcode},
+                borrowernumber => $patron->{borrowernumber},
+                biblionumber   => $biblio1->{biblionumber},
+                notes          => "a note",
+                itemnumber     => $item1->{itemnumber},
+            }
+        );
+        my $reserve_id2 = C4::Reserves::AddReserve(
+            {
+                branchcode     => $library->{branchcode},
+                borrowernumber => $patron->{borrowernumber},
+                biblionumber   => $biblio1->{biblionumber},
+                notes          => "a note",
+                itemnumber     => $item1->{itemnumber},
+            }
+        );
+        my $reserve_id3 = C4::Reserves::AddReserve(
+            {
+                branchcode     => $library->{branchcode},
+                borrowernumber => $patron->{borrowernumber},
+                biblionumber   => $biblio2->{biblionumber},
+                notes          => "another note",
+                itemnumber     => $item2->{itemnumber},
+            }
+        );
+
+        my $template = <<EOF;
+<h5>Date: <<today>></h5>
+
+<h3> Transfer to/Hold in <<branches.branchname>></h3>
+
+<h3><<borrowers.surname>>, <<borrowers.firstname>></h3>
+
+<ul>
+    <li><<borrowers.cardnumber>></li>
+    <li><<borrowers.phone>></li>
+    <li> <<borrowers.address>><br />
+         <<borrowers.address2>><br />
+         <<borrowers.city>>  <<borrowers.zipcode>>
+    </li>
+    <li><<borrowers.email>></li>
+</ul>
+<br />
+<h3>ITEM ON HOLD</h3>
+<h4><<biblio.title>></h4>
+<h5><<biblio.author>></h5>
+<ul>
+   <li><<items.barcode>></li>
+   <li><<items.itemcallnumber>></li>
+   <li><<reserves.waitingdate>></li>
+</ul>
+<p>Notes:
+<pre><<reserves.reserve_id>>=<<reserves.reservenotes>></pre>
+</p>
+EOF
+
+        reset_template( { template => $template, code => $code, module => 'circulation' } );
+        my $letter_for_item1 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, reserve_id => $reserve_id1 } );
+        my $letter_for_item2 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, reserve_id => $reserve_id3 } );
+
+        my $tt_template = <<EOF;
+<h5>Date: [% today | \$KohaDates with_hours => 1 %]</h5>
+
+<h3> Transfer to/Hold in [% branch.branchname %]</h3>
+
+<h3>[% borrower.surname %], [% borrower.firstname %]</h3>
+
+<ul>
+    <li>[% borrower.cardnumber %]</li>
+    <li>[% borrower.phone %]</li>
+    <li> [% borrower.address %]<br />
+         [% borrower.address2 %]<br />
+         [% borrower.city %]  [% borrower.zipcode %]
+    </li>
+    <li>[% borrower.email %]</li>
+</ul>
+<br />
+<h3>ITEM ON HOLD</h3>
+<h4>[% biblio.title %]</h4>
+<h5>[% biblio.author %]</h5>
+<ul>
+   <li>[% item.barcode %]</li>
+   <li>[% item.itemcallnumber %]</li>
+   <li>[% hold.waitingdate | \$KohaDates %]</li>
+</ul>
+<p>Notes:
+<pre>[% hold.reserve_id %]=[% hold.reservenotes %]</pre>
+</p>
+EOF
+
+        reset_template( { template => $tt_template, code => $code, module => 'circulation' } );
+        my $tt_letter_for_item1 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, reserve_id => $reserve_id1 } );
+        my $tt_letter_for_item2 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, reserve_id => $reserve_id3 } );
+
+        is( $tt_letter_for_item1->{content}, $letter_for_item1->{content}, );
+        is( $tt_letter_for_item2->{content}, $letter_for_item2->{content}, );
+    };
+
+    subtest 'ISSUESLIP|checkedout|repeat' => sub {
+        plan tests => 2;
+
+        my $code = 'ISSUESLIP';
+        my $now = dt_from_string;
+        my $one_minute_ago = dt_from_string->subtract( minutes => 1 );
+
+        my $branchcode = $library->{branchcode};
+
+        Koha::News->delete;
+        my $news_item = Koha::NewsItem->new({ branchcode => $branchcode, title => "A wonderful news", content => "This is the wonderful news." })->store;
+
+        # historic syntax
+        my $template = <<EOF;
+<h3><<branches.branchname>></h3>
+Checked out to <<borrowers.title>> <<borrowers.firstname>> <<borrowers.initials>> <<borrowers.surname>> <br />
+(<<borrowers.cardnumber>>) <br />
+
+<<today>><br />
+
+<h4>Checked out</h4>
+<checkedout>
+<p>
+<<biblio.title>> <br />
+Barcode: <<items.barcode>><br />
+Date due: <<issues.date_due | dateonly>><br />
+</p>
+</checkedout>
+
+<h4>Overdues</h4>
+<overdue>
+<p>
+<<biblio.title>> <br />
+Barcode: <<items.barcode>><br />
+Date due: <<issues.date_due | dateonly>><br />
+</p>
+</overdue>
+
+<hr>
+
+<h4 style="text-align: center; font-style:italic;">News</h4>
+<news>
+<div class="newsitem">
+<h5 style="margin-bottom: 1px; margin-top: 1px"><b><<opac_news.title>></b></h5>
+<p style="margin-bottom: 1px; margin-top: 1px"><<opac_news.content>></p>
+<p class="newsfooter" style="font-size: 8pt; font-style:italic; margin-bottom: 1px; margin-top: 1px">Posted on <<opac_news.published_on>></p>
+<hr />
+</div>
+</news>
+EOF
+
+        reset_template( { template => $template, code => $code, module => 'circulation' } );
+
+        my $checkout = C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
+        $checkout->set( { timestamp => $now, issuedate => $one_minute_ago } )->store;
+        my $first_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
+
+        $checkout = C4::Circulation::AddIssue( $patron, $item2->{barcode} ); # Add a second checkout
+        $checkout->set( { timestamp => $now, issuedate => $now } )->store;
+        my $yesterday = dt_from_string->subtract( days => 1 );
+        C4::Circulation::AddIssue( $patron, $item3->{barcode}, $yesterday ); # Add an overdue
+        my $second_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
+
+        # Cleanup
+        AddReturn( $item1->{barcode} );
+        AddReturn( $item2->{barcode} );
+        AddReturn( $item3->{barcode} );
+
+        # TT syntax
+        my $tt_template = <<EOF;
+<h3>[% branch.branchname %]</h3>
+Checked out to [% borrower.title %] [% borrower.firstname %] [% borrower.initials %] [% borrower.surname %] <br />
+([% borrower.cardnumber %]) <br />
+
+[% today | \$KohaDates with_hours => 1 %]<br />
+
+<h4>Checked out</h4>
+[% FOREACH checkout IN checkouts %]
+[%~ SET item = checkout.item %]
+[%~ SET biblio = checkout.item.biblio %]
+<p>
+[% biblio.title %] <br />
+Barcode: [% item.barcode %]<br />
+Date due: [% checkout.date_due | \$KohaDates %]<br />
+</p>
+[% END %]
+
+<h4>Overdues</h4>
+[% FOREACH overdue IN overdues %]
+[%~ SET item = overdue.item %]
+[%~ SET biblio = overdue.item.biblio %]
+<p>
+[% biblio.title %] <br />
+Barcode: [% item.barcode %]<br />
+Date due: [% overdue.date_due | \$KohaDates %]<br />
+</p>
+[% END %]
+
+<hr>
+
+<h4 style="text-align: center; font-style:italic;">News</h4>
+[% FOREACH n IN news %]
+<div class="newsitem">
+<h5 style="margin-bottom: 1px; margin-top: 1px"><b>[% n.title %]</b></h5>
+<p style="margin-bottom: 1px; margin-top: 1px">[% n.content %]</p>
+<p class="newsfooter" style="font-size: 8pt; font-style:italic; margin-bottom: 1px; margin-top: 1px">Posted on [% n.timestamp | \$KohaDates %]</p>
+<hr />
+</div>
+[% END %]
+EOF
+
+        reset_template( { template => $tt_template, code => $code, module => 'circulation' } );
+
+        $checkout = C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
+        $checkout->set( { timestamp => $now, issuedate => $one_minute_ago } )->store;
+        my $first_tt_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
+
+        $checkout = C4::Circulation::AddIssue( $patron, $item2->{barcode} ); # Add a second checkout
+        $checkout->set( { timestamp => $now, issuedate => $now } )->store;
+        C4::Circulation::AddIssue( $patron, $item3->{barcode}, $yesterday ); # Add an overdue
+        my $second_tt_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
+
+        # There is too many line breaks generated by the historic syntax
+        $second_slip->{content} =~ s|</p>\n\n\n<p>|</p>\n\n<p>|s;
+
+        is( $first_tt_slip->{content}, $first_slip->{content}, );
+        is( $second_tt_slip->{content}, $second_slip->{content}, );
+
+        # Cleanup
+        AddReturn( $item1->{barcode} );
+        AddReturn( $item2->{barcode} );
+        AddReturn( $item3->{barcode} );
+    };
+
+    subtest 'ODUE|items.content|item' => sub {
+        plan tests => 1;
+
+        my $code = 'ODUE';
+
+        my $branchcode = $library->{branchcode};
+
+        # historic syntax
+        # FIXME items.fine does not work with TT notices
+        # See bug 17976
+        # <item> should contain Fine: <<items.fine>></item>
+        my $template = <<EOF;
+Dear <<borrowers.firstname>> <<borrowers.surname>>,
+
+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.
+
+<<branches.branchname>>
+<<branches.branchaddress1>>
+<<branches.branchaddress2>> <<branches.branchaddress3>>
+Phone: <<branches.branchphone>>
+Fax: <<branches.branchfax>>
+Email: <<branches.branchemail>>
+
+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.
+
+The following item(s) is/are currently overdue:
+
+<item>"<<biblio.title>>" by <<biblio.author>>, <<items.itemcallnumber>>, Barcode: <<items.barcode>></item>
+
+<<items.content>>
+
+Thank-you for your prompt attention to this matter.
+
+<<branches.branchname>> Staff
+EOF
+
+        reset_template( { template => $template, code => $code, module => 'circulation' } );
+
+        my $yesterday = dt_from_string->subtract( days => 1 );
+        my $two_days_ago = dt_from_string->subtract( days => 2 );
+        my $issue1 = C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
+        my $issue2 = C4::Circulation::AddIssue( $patron, $item2->{barcode}, $yesterday ); # Add an first overdue
+        my $issue3 = C4::Circulation::AddIssue( $patron, $item3->{barcode}, $two_days_ago ); # Add an second overdue
+        $issue1 = $issue1->unblessed;
+        $issue2 = $issue2->unblessed;
+        $issue3 = $issue3->unblessed;
+
+        # For items.content
+        my @item_fields = qw( date_due title barcode author itemnumber );
+        my $items_content = C4::Letters::get_item_content( { item => { %$item1, %$biblio1, %$issue1 }, item_content_fields => \@item_fields, dateonly => 1 } );
+          $items_content .= C4::Letters::get_item_content( { item => { %$item2, %$biblio2, %$issue2 }, item_content_fields => \@item_fields, dateonly => 1 } );
+          $items_content .= C4::Letters::get_item_content( { item => { %$item3, %$biblio3, %$issue3 }, item_content_fields => \@item_fields, dateonly => 1 } );
+
+        my @items = ( $item1, $item2, $item3 );
+        my $letter = C4::Overdues::parse_overdues_letter(
+            {
+                letter_code => $code,
+                borrowernumber => $patron->{borrowernumber},
+                branchcode  => $library->{branchcode},
+                items       => \@items,
+                substitute  => {
+                    bib                    => $library->{branchname},
+                    'items.content'        => $items_content,
+                    count                  => scalar( @items ),
+                    message_transport_type => 'email',
+                }
+            }
+        );
+
+        # Cleanup
+        AddReturn( $item1->{barcode} );
+        AddReturn( $item2->{barcode} );
+        AddReturn( $item3->{barcode} );
+
+
+        # historic syntax
+        my $tt_template = <<EOF;
+Dear [% borrower.firstname %] [% borrower.surname %],
+
+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.
+
+[% branch.branchname %]
+[% branch.branchaddress1 %]
+[% branch.branchaddress2 %] [% branch.branchaddress3 %]
+Phone: [% branch.branchphone %]
+Fax: [% branch.branchfax %]
+Email: [% branch.branchemail %]
+
+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.
+
+The following item(s) is/are currently overdue:
+
+[% FOREACH overdue IN overdues %]
+[%~ SET item = overdue.item ~%]
+"[% item.biblio.title %]" by [% item.biblio.author %], [% item.itemcallnumber %], Barcode: [% item.barcode %]
+[% END %]
+[% FOREACH overdue IN overdues %]
+[%~ SET item = overdue.item ~%]
+[% overdue.date_due | \$KohaDates %]\t[% item.biblio.title %]\t[% item.barcode %]\t[% item.biblio.author %]\t[% item.itemnumber %]
+[% END %]
+
+Thank-you for your prompt attention to this matter.
+
+[% branch.branchname %] Staff
+EOF
+
+        reset_template( { template => $tt_template, code => $code, module => 'circulation' } );
+
+        C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
+        C4::Circulation::AddIssue( $patron, $item2->{barcode}, $yesterday ); # Add an first overdue
+        C4::Circulation::AddIssue( $patron, $item3->{barcode}, $two_days_ago ); # Add an second overdue
+
+        my $tt_letter = C4::Overdues::parse_overdues_letter(
+            {
+                letter_code => $code,
+                borrowernumber => $patron->{borrowernumber},
+                branchcode  => $library->{branchcode},
+                items       => \@items,
+                substitute  => {
+                    bib                    => $library->{branchname},
+                    'items.content'        => $items_content,
+                    count                  => scalar( @items ),
+                    message_transport_type => 'email',
+                }
+            }
+        );
+
+        is( $tt_letter->{content}, $letter->{content}, );
+    };
+
+    subtest 'Bug 19743 - Header and Footer should be updated on each item for checkin / checkout / renewal notices' => sub {
+        plan tests => 8;
+
+        my $checkout_code = 'CHECKOUT';
+        my $checkin_code = 'CHECKIN';
+
+        my $dbh = C4::Context->dbh;
+        $dbh->do("DELETE FROM letter");
+        $dbh->do("DELETE FROM issues");
+        $dbh->do("DELETE FROM message_queue");
+
+        # Enable notification for CHECKOUT - Things are hardcoded here but should work with default data
+        $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 6 );
+        my $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
+        $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
+        # Enable notification for CHECKIN - Things are hardcoded here but should work with default data
+        $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 5 );
+        $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
+        $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
+
+        my $checkout_template = q|
+<<branches.branchname>>
+----
+----
+|;
+        reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
+        my $checkin_template = q[
+<<branches.branchname>>
+----
+----
+];
+        reset_template( { template => $checkin_template, code => $checkin_code, module => 'circulation' } );
+
+        my $issue = C4::Circulation::AddIssue( $patron, $item1->{barcode} );
+        my $first_checkout_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
+
+        my $library_object = Koha::Libraries->find( $issue->branchcode );
+        my $old_branchname = $library_object->branchname;
+        my $new_branchname = "Kyle M Hall Memorial Library";
+
+        # Change branch name for second checkout notice
+        $library_object->branchname($new_branchname);
+        $library_object->store();
+
+        C4::Circulation::AddIssue( $patron, $item2->{barcode} );
+        my $second_checkout_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
+
+        # Restore old name for first checkin notice
+        $library_object->branchname( $old_branchname );
+        $library_object->store();
+
+        AddReturn( $item1->{barcode} );
+        my $first_checkin_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
+
+        # Change branch name for second checkin notice
+        $library_object->branchname($new_branchname);
+        $library_object->store();
+
+        AddReturn( $item2->{barcode} );
+        my $second_checkin_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
+
+        # Restore old name for first TT checkout notice
+        $library_object->branchname( $old_branchname );
+        $library_object->store();
+
+        Koha::Notice::Messages->delete;
+
+        # TT syntax
+        $checkout_template = q|
+[% branch.branchname %]
+----
+----
+|;
+        reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
+        $checkin_template = q[
+[% branch.branchname %]
+----
+----
+];
+        reset_template( { template => $checkin_template, code => $checkin_code, module => 'circulation' } );
+
+        C4::Circulation::AddIssue( $patron, $item1->{barcode} );
+        my $first_checkout_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
+
+        # Change branch name for second checkout notice
+        $library_object->branchname($new_branchname);
+        $library_object->store();
+
+        C4::Circulation::AddIssue( $patron, $item2->{barcode} );
+        my $second_checkout_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
+
+        # Restore old name for first checkin notice
+        $library_object->branchname( $old_branchname );
+        $library_object->store();
+
+        AddReturn( $item1->{barcode} );
+        my $first_checkin_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
+#
+        # Change branch name for second checkin notice
+        $library_object->branchname($new_branchname);
+        $library_object->store();
+
+        AddReturn( $item2->{barcode} );
+        my $second_checkin_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
+
+        my $first_letter = qq[
+$old_branchname
+];
+        my $second_letter = qq[
+$new_branchname
+];
+
+
+        is( $first_checkout_letter->content, $first_letter, 'Verify first checkout letter' );
+        is( $second_checkout_letter->content, $second_letter, 'Verify second checkout letter' );
+        is( $first_checkin_letter->content, $first_letter, 'Verify first checkin letter'  );
+        is( $second_checkin_letter->content, $second_letter, 'Verify second checkin letter' );
+
+        is( $first_checkout_tt_letter->content, $first_letter, 'Verify TT first checkout letter' );
+        is( $second_checkout_tt_letter->content, $second_letter, 'Verify TT second checkout letter' );
+        is( $first_checkin_tt_letter->content, $first_letter, 'Verify TT first checkin letter'  );
+        is( $second_checkin_tt_letter->content, $second_letter, 'Verify TT second checkin letter' );
+    };
+
 };
 
 subtest 'loops' => sub {
@@ -506,6 +1043,118 @@ subtest 'loops' => sub {
     };
 };
 
+subtest 'add_tt_filters' => sub {
+    plan tests => 1;
+    my $code   = "TEST";
+    my $module = "TEST";
+
+    my $patron = $builder->build_object(
+        {
+            class => 'Koha::Patrons',
+            value => { surname => "with_punctuation_" }
+        }
+    );
+    my $biblio = $builder->build_object(
+        { class => 'Koha::Biblios', value => { title => "with_punctuation_" } }
+    );
+    my $biblioitem = $builder->build_object(
+        {
+            class => 'Koha::Biblioitems',
+            value => {
+                biblionumber => $biblio->biblionumber,
+                isbn         => "with_punctuation_"
+            }
+        }
+    );
+
+    my $template = q|patron=[% borrower.surname %];biblio=[% biblio.title %];biblioitems=[% biblioitem.isbn %]|;
+    reset_template( { template => $template, code => $code, module => $module } );
+    my $letter = GetPreparedLetter(
+        module      => $module,
+        letter_code => $code,
+        tables      => {
+            borrowers   => $patron->borrowernumber,
+            biblio      => $biblio->biblionumber,
+            biblioitems => $biblioitem->biblioitemnumber
+        }
+    );
+    my $expected_letter = q|patron=with_punctuation_;biblio=with_punctuation;biblioitems=with_punctuation|;
+    is( $letter->{content}, $expected_letter, "Pre-processing should call TT plugin to remove punctuation if table is biblio or biblioitems");
+};
+
+subtest 'Handle includes' => sub {
+    plan tests => 1;
+    my $cgi = CGI->new();
+    my $code = 'TEST_INCLUDE';
+    my $account =
+      $builder->build_object( { class => 'Koha::Account::Lines', value => { credit_type_code => 'PAYMENT', status => 'CANCELLED' } } );
+    my $template = <<EOF;
+[%- USE Price -%]
+[%- PROCESS 'accounts.inc' -%]
+[%- PROCESS account_type_description account=credit -%]
+EOF
+    reset_template({ template => $template, code => $code, module => 'test' });
+    my $letter = GetPreparedLetter(
+        module      => 'test',
+        letter_code => $code,
+        tables      => {
+            credits => $account->accountlines_id
+        }
+    );
+    is($letter->{content},'    <span>Payment<span> (Cancelled)</span>    </span>', "Include used in notice");
+};
+
+subtest 'Dates formatting' => sub {
+    plan tests => 1;
+    my $code = 'TEST_DATE';
+    t::lib::Mocks::mock_preference('dateformat', 'metric'); # MM/DD/YYYY
+    my $biblio = $builder->build_object(
+        {
+            class => 'Koha::Biblios',
+            value => {
+                timestamp   => '2018-12-13 20:21:22',
+                datecreated => '2018-12-13'
+            }
+        }
+    );
+    my $template = <<EOF;
+[%- USE KohaDates -%]
+[% biblio.timestamp %]
+[% biblio.timestamp | \$KohaDates %]
+[% biblio.timestamp | \$KohaDates with_hours => 1 %]
+
+[% biblio.datecreated %]
+[% biblio.datecreated | \$KohaDates %]
+[% biblio.datecreated | \$KohaDates with_hours => 1 %]
+
+[% biblio.timestamp | \$KohaDates dateformat => 'iso' %]
+[% KohaDates.output_preference( str => biblio.timestamp, dateformat => 'iso' ) %]
+[% KohaDates.output_preference( str => biblio.timestamp, dateformat => 'iso', dateonly => 1 ) %]
+EOF
+    reset_template({ template => $template, code => $code, module => 'test' });
+    my $letter = GetPreparedLetter(
+        module => 'test',
+        letter_code => $code,
+        tables => {
+            biblio => $biblio->biblionumber,
+        }
+    );
+    my $expected_content = sprintf("%s\n%s\n%s\n\n%s\n%s\n%s\n\n%s\n%s\n%s\n",
+        '2018-12-13 20:21:22',
+        '13/12/2018',
+        '13/12/2018 20:21',
+
+        '2018-12-13',
+        '13/12/2018',
+        '13/12/2018 00:00',
+
+        '2018-12-13',
+        '2018-12-13 20:21',
+        '2018-12-13',
+    );
+    is( $letter->{content}, $expected_content );
+};
+
 sub reset_template {
     my ( $params ) = @_;
     my $template   = $params->{template};
@@ -546,3 +1195,5 @@ sub process_letter {
     );
     return $letter;
 }
+
+$schema->storage->txn_rollback;