3 # This file is part of Koha.
5 # Copyright (C) 2016 ByWater Solutions
6 # Copyright (C) 2017 Koha Development Team
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.
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.
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>.
22 use Test::More tests => 18;
27 use t::lib::TestBuilder;
35 use Koha::ArticleRequests;
42 use Koha::Subscription;
45 use Koha::Notice::Messages;
46 use Koha::Notice::Templates;
47 use Koha::Patron::Modification;
49 my $schema = Koha::Database->schema;
50 $schema->storage->txn_begin();
52 my $builder = t::lib::TestBuilder->new();
54 my $dbh = C4::Context->dbh;
55 $dbh->{RaiseError} = 1;
57 $dbh->do(q|DELETE FROM letter|);
59 my $date = dt_from_string;
61 my $library = $builder->build( { source => 'Branch' } );
62 my $patron = $builder->build( { source => 'Borrower' } );
63 my $patron2 = $builder->build( { source => 'Borrower' } );
65 my $biblio = Koha::Biblio->new(
67 title => 'Test Biblio'
71 my $biblioitem = Koha::Biblioitem->new(
73 biblionumber => $biblio->id()
77 my $item = Koha::Item->new(
79 biblionumber => $biblio->id(),
80 biblioitemnumber => $biblioitem->id()
84 my $hold = Koha::Hold->new(
86 borrowernumber => $patron->{borrowernumber},
87 biblionumber => $biblio->id()
91 my $news = Koha::NewsItem->new({ title => 'a news title', content => 'a news content'})->store();
92 my $serial = Koha::Serial->new()->store();
93 my $subscription = Koha::Subscription->new()->store();
94 my $suggestion = Koha::Suggestion->new()->store();
95 my $checkout = Koha::Checkout->new( { itemnumber => $item->id() } )->store();
96 my $modification = Koha::Patron::Modification->new( { verification_token => "TEST" } )->store();
101 $dbh->prepare(q{INSERT INTO letter (module, code, name, title, content) VALUES ('test',?,'Test','Test',?)});
103 $sth->execute( "TEST_PATRON", "[% borrower.id %]" );
104 $prepared_letter = GetPreparedLetter(
107 letter_code => 'TEST_PATRON',
109 borrowers => $patron->{borrowernumber},
113 is( $prepared_letter->{content}, $patron->{borrowernumber}, 'Patron object used correctly with scalar' );
115 $prepared_letter = GetPreparedLetter(
118 letter_code => 'TEST_PATRON',
120 borrowers => $patron,
124 is( $prepared_letter->{content}, $patron->{borrowernumber}, 'Patron object used correctly with hashref' );
126 $prepared_letter = GetPreparedLetter(
129 letter_code => 'TEST_PATRON',
131 borrowers => [ $patron->{borrowernumber} ],
135 is( $prepared_letter->{content}, $patron->{borrowernumber}, 'Patron object used correctly with arrayref' );
137 $sth->execute( "TEST_BIBLIO", "[% biblio.id %]" );
138 $prepared_letter = GetPreparedLetter(
141 letter_code => 'TEST_BIBLIO',
143 biblio => $biblio->id(),
147 is( $prepared_letter->{content}, $biblio->id, 'Biblio object used correctly' );
149 $sth->execute( "TEST_LIBRARY", "[% branch.id %]" );
150 $prepared_letter = GetPreparedLetter(
153 letter_code => 'TEST_LIBRARY',
155 branches => $library->{branchcode}
159 is( $prepared_letter->{content}, $library->{branchcode}, 'Library object used correctly' );
161 $sth->execute( "TEST_ITEM", "[% item.id %]" );
162 $prepared_letter = GetPreparedLetter(
165 letter_code => 'TEST_ITEM',
171 is( $prepared_letter->{content}, $item->id(), 'Item object used correctly' );
173 $sth->execute( "TEST_NEWS", "[% news.id %]" );
174 $prepared_letter = GetPreparedLetter(
177 letter_code => 'TEST_NEWS',
179 opac_news => $news->id()
183 is( $prepared_letter->{content}, $news->id(), 'News object used correctly' );
185 $sth->execute( "TEST_HOLD", "[% hold.id %]" );
186 $prepared_letter = GetPreparedLetter(
189 letter_code => 'TEST_HOLD',
191 reserves => { borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio->id() },
195 is( $prepared_letter->{content}, $hold->id(), 'Hold object used correctly' );
198 $prepared_letter = GetPreparedLetter(
201 letter_code => 'TEST_HOLD',
203 reserves => [ $patron->{borrowernumber}, $biblio->id() ],
209 like( $croak, qr{^Multiple foreign keys \(table reserves\) should be passed using an hashref.*}, "GetPreparedLetter should not be called with arrayref for multiple FK" );
212 $prepared_letter = GetPreparedLetter(
215 letter_code => 'TEST_HOLD',
217 'branches' => $library,
218 'borrowers' => $patron,
219 'biblio' => $biblio->id,
220 'biblioitems' => $biblioitem->id,
221 'reserves' => $hold->unblessed,
222 'items' => $hold->itemnumber,
226 is( $prepared_letter->{content}, $hold->id(), 'Hold object used correctly' );
228 $sth->execute( "TEST_SERIAL", "[% serial.id %]" );
229 $prepared_letter = GetPreparedLetter(
232 letter_code => 'TEST_SERIAL',
234 serial => $serial->id()
238 is( $prepared_letter->{content}, $serial->id(), 'Serial object used correctly' );
240 $sth->execute( "TEST_SUBSCRIPTION", "[% subscription.id %]" );
241 $prepared_letter = GetPreparedLetter(
244 letter_code => 'TEST_SUBSCRIPTION',
246 subscription => $subscription->id()
250 is( $prepared_letter->{content}, $subscription->id(), 'Subscription object used correctly' );
252 $sth->execute( "TEST_SUGGESTION", "[% suggestion.id %]" );
253 $prepared_letter = GetPreparedLetter(
256 letter_code => 'TEST_SUGGESTION',
258 suggestions => $suggestion->id()
262 is( $prepared_letter->{content}, $suggestion->id(), 'Suggestion object used correctly' );
264 $sth->execute( "TEST_ISSUE", "[% checkout.id %]" );
265 $prepared_letter = GetPreparedLetter(
268 letter_code => 'TEST_ISSUE',
270 issues => $item->id()
274 is( $prepared_letter->{content}, $checkout->id(), 'Checkout object used correctly' );
276 $sth->execute( "TEST_MODIFICATION", "[% patron_modification.id %]" );
277 $prepared_letter = GetPreparedLetter(
280 letter_code => 'TEST_MODIFICATION',
282 borrower_modifications => $modification->verification_token,
286 is( $prepared_letter->{content}, $modification->id(), 'Patron modification object used correctly' );
288 subtest 'regression tests' => sub {
291 my $library = $builder->build( { source => 'Branch' } );
292 my $patron = $builder->build( { source => 'Borrower' } );
293 my $biblio1 = Koha::Biblio->new({title => 'Test Biblio 1', author => 'An author', })->store->unblessed;
294 my $biblioitem1 = Koha::Biblioitem->new({biblionumber => $biblio1->{biblionumber}})->store()->unblessed;
295 my $item1 = Koha::Item->new(
297 biblionumber => $biblio1->{biblionumber},
298 biblioitemnumber => $biblioitem1->{biblioitemnumber},
299 barcode => 'a_t_barcode',
300 homebranch => $library->{branchcode},
301 holdingbranch => $library->{branchcode},
303 itemcallnumber => 'itemcallnumber1',
306 my $biblio2 = Koha::Biblio->new({title => 'Test Biblio 2'})->store->unblessed;
307 my $biblioitem2 = Koha::Biblioitem->new({biblionumber => $biblio2->{biblionumber}})->store()->unblessed;
308 my $item2 = Koha::Item->new(
310 biblionumber => $biblio2->{biblionumber},
311 biblioitemnumber => $biblioitem2->{biblioitemnumber},
312 barcode => 'another_t_barcode',
313 homebranch => $library->{branchcode},
314 holdingbranch => $library->{branchcode},
316 itemcallnumber => 'itemcallnumber2',
319 my $biblio3 = Koha::Biblio->new({title => 'Test Biblio 3'})->store->unblessed;
320 my $biblioitem3 = Koha::Biblioitem->new({biblionumber => $biblio3->{biblionumber}})->store()->unblessed;
321 my $item3 = Koha::Item->new(
323 biblionumber => $biblio3->{biblionumber},
324 biblioitemnumber => $biblioitem3->{biblioitemnumber},
325 barcode => 'another_t_barcode_3',
326 homebranch => $library->{branchcode},
327 holdingbranch => $library->{branchcode},
329 itemcallnumber => 'itemcallnumber3',
333 C4::Context->_new_userenv('xxx');
334 C4::Context->set_userenv(0,0,0,'firstname','surname', $library->{branchcode}, 'Midway Public Library', '', '', '');
336 subtest 'ACQ_NOTIF_ON_RECEIV ' => sub {
338 my $code = 'ACQ_NOTIF_ON_RECEIV';
339 my $branchcode = $library->{branchcode};
340 my $order = $builder->build({ source => 'Aqorder' });
343 Dear <<borrowers.firstname>> <<borrowers.surname>>,
344 The order <<aqorders.ordernumber>> (<<biblio.title>>) has been received.
347 my $params = { code => $code, branchcode => $branchcode, tables => { branches => $library, borrowers => $patron, biblio => $biblio1, aqorders => $order } };
348 my $letter = process_letter( { template => $template, %$params });
350 Dear [% borrower.firstname %] [% borrower.surname %],
351 The order [% order.ordernumber %] ([% biblio.title %]) has been received.
354 my $tt_letter = process_letter( { template => $tt_template, %$params });
356 is( $tt_letter->{content}, $letter->{content}, 'Verified letter content' );
359 subtest 'AR_*' => sub {
361 my $code = 'AR_CANCELED';
362 my $branchcode = $library->{branchcode};
365 <<borrowers.firstname>> <<borrowers.surname>> (<<borrowers.cardnumber>>)
367 Your request for an article from <<biblio.title>> (<<items.barcode>>) has been canceled for the following reason:
369 <<article_requests.notes>>
372 Title: <<article_requests.title>>
373 Author: <<article_requests.author>>
374 Volume: <<article_requests.volume>>
375 Issue: <<article_requests.issue>>
376 Date: <<article_requests.date>>
377 Pages: <<article_requests.pages>>
378 Chapters: <<article_requests.chapters>>
379 Notes: <<article_requests.patron_notes>>
381 reset_template( { template => $template, code => $code, module => 'circulation' } );
382 my $article_request = $builder->build({ source => 'ArticleRequest' });
383 Koha::ArticleRequests->find( $article_request->{id} )->cancel;
384 my $letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
387 [% borrower.firstname %] [% borrower.surname %] ([% borrower.cardnumber %])
389 Your request for an article from [% biblio.title %] ([% item.barcode %]) has been canceled for the following reason:
391 [% article_request.notes %]
394 Title: [% article_request.title %]
395 Author: [% article_request.author %]
396 Volume: [% article_request.volume %]
397 Issue: [% article_request.issue %]
398 Date: [% article_request.date %]
399 Pages: [% article_request.pages %]
400 Chapters: [% article_request.chapters %]
401 Notes: [% article_request.patron_notes %]
403 reset_template( { template => $tt_template, code => $code, module => 'circulation' } );
404 Koha::ArticleRequests->find( $article_request->{id} )->cancel;
405 my $tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
406 is( $tt_letter->content, $letter->content, 'Compare AR_* notices' );
407 isnt( $tt_letter->message_id, $letter->message_id, 'Comparing AR_* notices should compare 2 different messages' );
410 subtest 'CHECKOUT+CHECKIN' => sub {
413 my $checkout_code = 'CHECKOUT';
414 my $checkin_code = 'CHECKIN';
416 my $dbh = C4::Context->dbh;
417 # Enable notification for CHECKOUT - Things are hardcoded here but should work with default data
418 $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 6 );
419 my $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
420 $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
421 # Enable notification for CHECKIN - Things are hardcoded here but should work with default data
422 $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 5 );
423 $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
424 $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
427 my $checkout_template = q|
428 The following items have been checked out:
432 Thank you for visiting <<branches.branchname>>.
434 reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
435 my $checkin_template = q[
436 The following items have been checked out:
438 <<biblio.title>> was due on <<old_issues.date_due | dateonly>>
440 Thank you for visiting <<branches.branchname>>.
442 reset_template( { template => $checkin_template, code => $checkin_code, module => 'circulation' } );
444 C4::Circulation::AddIssue( $patron, $item1->{barcode} );
445 my $first_checkout_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
446 C4::Circulation::AddIssue( $patron, $item2->{barcode} );
447 my $second_checkout_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
449 AddReturn( $item1->{barcode} );
450 my $first_checkin_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
451 AddReturn( $item2->{barcode} );
452 my $second_checkin_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
454 Koha::Notice::Messages->delete;
457 $checkout_template = q|
458 The following items have been checked out:
462 Thank you for visiting [% branch.branchname %].
464 reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
465 $checkin_template = q[
466 The following items have been checked out:
468 [% biblio.title %] was due on [% old_checkout.date_due | $KohaDates %]
470 Thank you for visiting [% branch.branchname %].
472 reset_template( { template => $checkin_template, code => $checkin_code, module => 'circulation' } );
474 C4::Circulation::AddIssue( $patron, $item1->{barcode} );
475 my $first_checkout_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
476 C4::Circulation::AddIssue( $patron, $item2->{barcode} );
477 my $second_checkout_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
479 AddReturn( $item1->{barcode} );
480 my $first_checkin_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
481 AddReturn( $item2->{barcode} );
482 my $second_checkin_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
484 is( $first_checkout_tt_letter->content, $first_checkout_letter->content, 'Verify first checkout letter' );
485 is( $second_checkout_tt_letter->content, $second_checkout_letter->content, 'Verify second checkout letter' );
486 is( $first_checkin_tt_letter->content, $first_checkin_letter->content, 'Verify first checkin letter' );
487 is( $second_checkin_tt_letter->content, $second_checkin_letter->content, 'Verify second checkin letter' );
491 subtest 'DUEDGST|count' => sub {
494 my $code = 'DUEDGST';
496 my $dbh = C4::Context->dbh;
497 # Enable notification for DUEDGST - Things are hardcoded here but should work with default data
498 $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 1 );
499 my $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
500 $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
504 substitute => { count => 42 },
508 You have <<count>> items due
510 my $letter = process_letter( { template => $template, %$params });
513 You have [% count %] items due
515 my $tt_letter = process_letter( { template => $tt_template, %$params });
516 is( $tt_letter->{content}, $letter->{content}, );
519 subtest 'HOLD_SLIP|dates|today' => sub {
522 my $code = 'HOLD_SLIP';
524 C4::Reserves::AddReserve( $library->{branchcode}, $patron->{borrowernumber}, $biblio1->{biblionumber}, undef, undef, undef, undef, "a note", undef, $item1->{itemnumber}, 'W' );
525 C4::Reserves::AddReserve( $library->{branchcode}, $patron->{borrowernumber}, $biblio2->{biblionumber}, undef, undef, undef, undef, "another note", undef, $item2->{itemnumber} );
527 my $template = <<EOF;
528 <h5>Date: <<today>></h5>
530 <h3> Transfer to/Hold in <<branches.branchname>></h3>
532 <h3><<borrowers.surname>>, <<borrowers.firstname>></h3>
535 <li><<borrowers.cardnumber>></li>
536 <li><<borrowers.phone>></li>
537 <li> <<borrowers.address>><br />
538 <<borrowers.address2>><br />
539 <<borrowers.city>> <<borrowers.zipcode>>
541 <li><<borrowers.email>></li>
544 <h3>ITEM ON HOLD</h3>
545 <h4><<biblio.title>></h4>
546 <h5><<biblio.author>></h5>
548 <li><<items.barcode>></li>
549 <li><<items.itemcallnumber>></li>
550 <li><<reserves.waitingdate>></li>
553 <pre><<reserves.reservenotes>></pre>
557 reset_template( { template => $template, code => $code, module => 'circulation' } );
558 my $letter_for_item1 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio1->{biblionumber} } );
559 my $letter_for_item2 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio2->{biblionumber} } );
561 my $tt_template = <<EOF;
562 <h5>Date: [% today | \$KohaDates with_hours => 1 %]</h5>
564 <h3> Transfer to/Hold in [% branch.branchname %]</h3>
566 <h3>[% borrower.surname %], [% borrower.firstname %]</h3>
569 <li>[% borrower.cardnumber %]</li>
570 <li>[% borrower.phone %]</li>
571 <li> [% borrower.address %]<br />
572 [% borrower.address2 %]<br />
573 [% borrower.city %] [% borrower.zipcode %]
575 <li>[% borrower.email %]</li>
578 <h3>ITEM ON HOLD</h3>
579 <h4>[% biblio.title %]</h4>
580 <h5>[% biblio.author %]</h5>
582 <li>[% item.barcode %]</li>
583 <li>[% item.itemcallnumber %]</li>
584 <li>[% hold.waitingdate | \$KohaDates %]</li>
587 <pre>[% hold.reservenotes %]</pre>
591 reset_template( { template => $tt_template, code => $code, module => 'circulation' } );
592 my $tt_letter_for_item1 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio1->{biblionumber} } );
593 my $tt_letter_for_item2 = C4::Reserves::ReserveSlip( { branchcode => $library->{branchcode}, borrowernumber => $patron->{borrowernumber}, biblionumber => $biblio2->{biblionumber} } );
595 is( $tt_letter_for_item1->{content}, $letter_for_item1->{content}, );
596 is( $tt_letter_for_item2->{content}, $letter_for_item2->{content}, );
599 subtest 'ISSUESLIP|checkedout|repeat' => sub {
602 my $code = 'ISSUESLIP';
603 my $now = dt_from_string;
604 my $one_minute_ago = dt_from_string->subtract( minutes => 1 );
606 my $branchcode = $library->{branchcode};
609 my $news_item = Koha::NewsItem->new({ branchcode => $branchcode, title => "A wonderful news", content => "This is the wonderful news." })->store;
612 my $template = <<EOF;
613 <h3><<branches.branchname>></h3>
614 Checked out to <<borrowers.title>> <<borrowers.firstname>> <<borrowers.initials>> <<borrowers.surname>> <br />
615 (<<borrowers.cardnumber>>) <br />
622 <<biblio.title>> <br />
623 Barcode: <<items.barcode>><br />
624 Date due: <<issues.date_due | dateonly>><br />
631 <<biblio.title>> <br />
632 Barcode: <<items.barcode>><br />
633 Date due: <<issues.date_due | dateonly>><br />
639 <h4 style="text-align: center; font-style:italic;">News</h4>
641 <div class="newsitem">
642 <h5 style="margin-bottom: 1px; margin-top: 1px"><b><<opac_news.title>></b></h5>
643 <p style="margin-bottom: 1px; margin-top: 1px"><<opac_news.content>></p>
644 <p class="newsfooter" style="font-size: 8pt; font-style:italic; margin-bottom: 1px; margin-top: 1px">Posted on <<opac_news.timestamp>></p>
650 reset_template( { template => $template, code => $code, module => 'circulation' } );
652 my $checkout = C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
653 $checkout->set_columns( { timestamp => $now, issuedate => $one_minute_ago } )->update; # FIXME $checkout is a Koha::Schema::Result::Issues, must be a Koha::Checkout
654 my $first_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
656 $checkout = C4::Circulation::AddIssue( $patron, $item2->{barcode} ); # Add a second checkout
657 $checkout->set_columns( { timestamp => $now, issuedate => $now } )->update;
658 my $yesterday = dt_from_string->subtract( days => 1 );
659 C4::Circulation::AddIssue( $patron, $item3->{barcode}, $yesterday ); # Add an overdue
660 my $second_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
663 AddReturn( $item1->{barcode} );
664 AddReturn( $item2->{barcode} );
665 AddReturn( $item3->{barcode} );
668 my $tt_template = <<EOF;
669 <h3>[% branch.branchname %]</h3>
670 Checked out to [% borrower.title %] [% borrower.firstname %] [% borrower.initials %] [% borrower.surname %] <br />
671 ([% borrower.cardnumber %]) <br />
673 [% today | \$KohaDates with_hours => 1 %]<br />
676 [% FOREACH checkout IN checkouts %]
677 [%~ SET item = checkout.item %]
678 [%~ SET biblio = checkout.item.biblio %]
680 [% biblio.title %] <br />
681 Barcode: [% item.barcode %]<br />
682 Date due: [% checkout.date_due | \$KohaDates %]<br />
687 [% FOREACH overdue IN overdues %]
688 [%~ SET item = overdue.item %]
689 [%~ SET biblio = overdue.item.biblio %]
691 [% biblio.title %] <br />
692 Barcode: [% item.barcode %]<br />
693 Date due: [% overdue.date_due | \$KohaDates %]<br />
699 <h4 style="text-align: center; font-style:italic;">News</h4>
700 [% FOREACH n IN news %]
701 <div class="newsitem">
702 <h5 style="margin-bottom: 1px; margin-top: 1px"><b>[% n.title %]</b></h5>
703 <p style="margin-bottom: 1px; margin-top: 1px">[% n.content %]</p>
704 <p class="newsfooter" style="font-size: 8pt; font-style:italic; margin-bottom: 1px; margin-top: 1px">Posted on [% n.timestamp | \$KohaDates %]</p>
710 reset_template( { template => $tt_template, code => $code, module => 'circulation' } );
712 $checkout = C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
713 $checkout->set_columns( { timestamp => $now, issuedate => $one_minute_ago } )->update;
714 my $first_tt_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
716 $checkout = C4::Circulation::AddIssue( $patron, $item2->{barcode} ); # Add a second checkout
717 $checkout->set_columns( { timestamp => $now, issuedate => $now } )->update;
718 C4::Circulation::AddIssue( $patron, $item3->{barcode}, $yesterday ); # Add an overdue
719 my $second_tt_slip = C4::Members::IssueSlip( $branchcode, $patron->{borrowernumber} );
721 # There is too many line breaks generated by the historic syntax
722 $second_slip->{content} =~ s|</p>\n\n\n<p>|</p>\n\n<p>|s;
724 is( $first_tt_slip->{content}, $first_slip->{content}, );
725 is( $second_tt_slip->{content}, $second_slip->{content}, );
728 AddReturn( $item1->{barcode} );
729 AddReturn( $item2->{barcode} );
730 AddReturn( $item3->{barcode} );
733 subtest 'ODUE|items.content|item' => sub {
738 my $branchcode = $library->{branchcode};
741 # FIXME items.fine does not work with TT notices
743 # <item> should contain Fine: <<items.fine>></item>
744 my $template = <<EOF;
745 Dear <<borrowers.firstname>> <<borrowers.surname>>,
747 According to our current records, you have items that are overdue.Your library does not charge late fines, but please return or renew them at the branch below as soon as possible.
749 <<branches.branchname>>
750 <<branches.branchaddress1>>
751 <<branches.branchaddress2>> <<branches.branchaddress3>>
752 Phone: <<branches.branchphone>>
753 Fax: <<branches.branchfax>>
754 Email: <<branches.branchemail>>
756 If you have registered a password with the library, and you have a renewal available, you may renew online. If an item becomes more than 30 days overdue, you will be unable to use your library card until the item is returned.
758 The following item(s) is/are currently overdue:
760 <item>"<<biblio.title>>" by <<biblio.author>>, <<items.itemcallnumber>>, Barcode: <<items.barcode>></item>
764 Thank-you for your prompt attention to this matter.
766 <<branches.branchname>> Staff
769 reset_template( { template => $template, code => $code, module => 'circulation' } );
771 my $yesterday = dt_from_string->subtract( days => 1 );
772 my $two_days_ago = dt_from_string->subtract( days => 2 );
773 my $issue1 = C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
774 my $issue2 = C4::Circulation::AddIssue( $patron, $item2->{barcode}, $yesterday ); # Add an first overdue
775 my $issue3 = C4::Circulation::AddIssue( $patron, $item3->{barcode}, $two_days_ago ); # Add an second overdue
776 $issue1 = Koha::Checkout->_new_from_dbic( $issue1 )->unblessed; # ->unblessed should be enough but AddIssue does not return a Koha::Checkout object
777 $issue2 = Koha::Checkout->_new_from_dbic( $issue2 )->unblessed;
778 $issue3 = Koha::Checkout->_new_from_dbic( $issue3 )->unblessed;
781 my @item_fields = qw( date_due title barcode author itemnumber );
782 my $items_content = C4::Letters::get_item_content( { item => { %$item1, %$biblio1, %$issue1 }, item_content_fields => \@item_fields, dateonly => 1 } );
783 $items_content .= C4::Letters::get_item_content( { item => { %$item2, %$biblio2, %$issue2 }, item_content_fields => \@item_fields, dateonly => 1 } );
784 $items_content .= C4::Letters::get_item_content( { item => { %$item3, %$biblio3, %$issue3 }, item_content_fields => \@item_fields, dateonly => 1 } );
786 my @items = ( $item1, $item2, $item3 );
787 my $letter = C4::Overdues::parse_overdues_letter(
789 letter_code => $code,
790 borrowernumber => $patron->{borrowernumber},
791 branchcode => $library->{branchcode},
794 bib => $library->{branchname},
795 'items.content' => $items_content,
796 count => scalar( @items ),
797 message_transport_type => 'email',
803 AddReturn( $item1->{barcode} );
804 AddReturn( $item2->{barcode} );
805 AddReturn( $item3->{barcode} );
809 my $tt_template = <<EOF;
810 Dear [% borrower.firstname %] [% borrower.surname %],
812 According to our current records, you have items that are overdue.Your library does not charge late fines, but please return or renew them at the branch below as soon as possible.
814 [% branch.branchname %]
815 [% branch.branchaddress1 %]
816 [% branch.branchaddress2 %] [% branch.branchaddress3 %]
817 Phone: [% branch.branchphone %]
818 Fax: [% branch.branchfax %]
819 Email: [% branch.branchemail %]
821 If you have registered a password with the library, and you have a renewal available, you may renew online. If an item becomes more than 30 days overdue, you will be unable to use your library card until the item is returned.
823 The following item(s) is/are currently overdue:
825 [% FOREACH overdue IN overdues %]
826 [%~ SET item = overdue.item ~%]
827 "[% item.biblio.title %]" by [% item.biblio.author %], [% item.itemcallnumber %], Barcode: [% item.barcode %]
829 [% FOREACH overdue IN overdues %]
830 [%~ SET item = overdue.item ~%]
831 [% overdue.date_due | \$KohaDates %]\t[% item.biblio.title %]\t[% item.barcode %]\t[% item.biblio.author %]\t[% item.itemnumber %]
834 Thank-you for your prompt attention to this matter.
836 [% branch.branchname %] Staff
839 reset_template( { template => $tt_template, code => $code, module => 'circulation' } );
841 C4::Circulation::AddIssue( $patron, $item1->{barcode} ); # Add a first checkout
842 C4::Circulation::AddIssue( $patron, $item2->{barcode}, $yesterday ); # Add an first overdue
843 C4::Circulation::AddIssue( $patron, $item3->{barcode}, $two_days_ago ); # Add an second overdue
845 my $tt_letter = C4::Overdues::parse_overdues_letter(
847 letter_code => $code,
848 borrowernumber => $patron->{borrowernumber},
849 branchcode => $library->{branchcode},
852 bib => $library->{branchname},
853 'items.content' => $items_content,
854 count => scalar( @items ),
855 message_transport_type => 'email',
860 is( $tt_letter->{content}, $letter->{content}, );
863 subtest 'Bug 19743 - Header and Footer should be updated on each item for checkin / checkout / renewal notices' => sub {
866 my $checkout_code = 'CHECKOUT';
867 my $checkin_code = 'CHECKIN';
869 my $dbh = C4::Context->dbh;
870 $dbh->do("DELETE FROM letter");
871 $dbh->do("DELETE FROM issues");
872 $dbh->do("DELETE FROM message_queue");
874 # Enable notification for CHECKOUT - Things are hardcoded here but should work with default data
875 $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 6 );
876 my $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
877 $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
878 # Enable notification for CHECKIN - Things are hardcoded here but should work with default data
879 $dbh->do(q|INSERT INTO borrower_message_preferences( borrowernumber, message_attribute_id ) VALUES ( ?, ? )|, undef, $patron->{borrowernumber}, 5 );
880 $borrower_message_preference_id = $dbh->last_insert_id(undef, undef, "borrower_message_preferences", undef);
881 $dbh->do(q|INSERT INTO borrower_message_transport_preferences( borrower_message_preference_id, message_transport_type) VALUES ( ?, ? )|, undef, $borrower_message_preference_id, 'email' );
883 my $checkout_template = q|
884 <<branches.branchname>>
888 reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
889 my $checkin_template = q[
890 <<branches.branchname>>
894 reset_template( { template => $checkin_template, code => $checkin_code, module => 'circulation' } );
896 my $issue = C4::Circulation::AddIssue( $patron, $item1->{barcode} );
897 my $first_checkout_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
899 my $library_object = Koha::Libraries->find( $issue->branchcode );
900 my $old_branchname = $library_object->branchname;
901 my $new_branchname = "Kyle M Hall Memorial Library";
903 # Change branch name for second checkout notice
904 $library_object->branchname($new_branchname);
905 $library_object->store();
907 C4::Circulation::AddIssue( $patron, $item2->{barcode} );
908 my $second_checkout_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
910 # Restore old name for first checkin notice
911 $library_object->branchname( $old_branchname );
912 $library_object->store();
914 AddReturn( $item1->{barcode} );
915 my $first_checkin_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
917 # Change branch name for second checkin notice
918 $library_object->branchname($new_branchname);
919 $library_object->store();
921 AddReturn( $item2->{barcode} );
922 my $second_checkin_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
924 # Restore old name for first TT checkout notice
925 $library_object->branchname( $old_branchname );
926 $library_object->store();
928 Koha::Notice::Messages->delete;
931 $checkout_template = q|
932 [% branch.branchname %]
936 reset_template( { template => $checkout_template, code => $checkout_code, module => 'circulation' } );
937 $checkin_template = q[
938 [% branch.branchname %]
942 reset_template( { template => $checkin_template, code => $checkin_code, module => 'circulation' } );
944 C4::Circulation::AddIssue( $patron, $item1->{barcode} );
945 my $first_checkout_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
947 # Change branch name for second checkout notice
948 $library_object->branchname($new_branchname);
949 $library_object->store();
951 C4::Circulation::AddIssue( $patron, $item2->{barcode} );
952 my $second_checkout_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
954 # Restore old name for first checkin notice
955 $library_object->branchname( $old_branchname );
956 $library_object->store();
958 AddReturn( $item1->{barcode} );
959 my $first_checkin_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
961 # Change branch name for second checkin notice
962 $library_object->branchname($new_branchname);
963 $library_object->store();
965 AddReturn( $item2->{barcode} );
966 my $second_checkin_tt_letter = Koha::Notice::Messages->search( {}, { order_by => { -desc => 'message_id' } } )->next;
968 my $first_letter = qq[
971 my $second_letter = qq[
976 is( $first_checkout_letter->content, $first_letter, 'Verify first checkout letter' );
977 is( $second_checkout_letter->content, $second_letter, 'Verify second checkout letter' );
978 is( $first_checkin_letter->content, $first_letter, 'Verify first checkin letter' );
979 is( $second_checkin_letter->content, $second_letter, 'Verify second checkin letter' );
981 is( $first_checkout_tt_letter->content, $first_letter, 'Verify TT first checkout letter' );
982 is( $second_checkout_tt_letter->content, $second_letter, 'Verify TT second checkout letter' );
983 is( $first_checkin_tt_letter->content, $first_letter, 'Verify TT first checkin letter' );
984 is( $second_checkin_tt_letter->content, $second_letter, 'Verify TT second checkin letter' );
989 subtest 'loops' => sub {
994 subtest 'primary key is AI' => sub {
996 my $patron_1 = $builder->build({ source => 'Borrower' });
997 my $patron_2 = $builder->build({ source => 'Borrower' });
999 my $template = q|[% FOREACH patron IN borrowers %][% patron.surname %][% END %]|;
1000 reset_template( { template => $template, code => $code, module => $module } );
1001 my $letter = GetPreparedLetter( module => $module, letter_code => $code, loops => { borrowers => [ $patron_1->{borrowernumber}, $patron_2->{borrowernumber} ] } );
1002 my $expected_letter = join '', ( $patron_1->{surname}, $patron_2->{surname} );
1003 is( $letter->{content}, $expected_letter, );
1006 subtest 'foreign key is used' => sub {
1008 my $patron_1 = $builder->build({ source => 'Borrower' });
1009 my $patron_2 = $builder->build({ source => 'Borrower' });
1010 my $checkout_1 = $builder->build({ source => 'Issue', value => { borrowernumber => $patron_1->{borrowernumber} } } );
1011 my $checkout_2 = $builder->build({ source => 'Issue', value => { borrowernumber => $patron_1->{borrowernumber} } } );
1013 my $template = q|[% FOREACH checkout IN checkouts %][% checkout.issue_id %][% END %]|;
1014 reset_template( { template => $template, code => $code, module => $module } );
1015 my $letter = GetPreparedLetter( module => $module, letter_code => $code, loops => { issues => [ $checkout_1->{itemnumber}, $checkout_2->{itemnumber} ] } );
1016 my $expected_letter = join '', ( $checkout_1->{issue_id}, $checkout_2->{issue_id} );
1017 is( $letter->{content}, $expected_letter, );
1021 subtest 'add_tt_filters' => sub {
1024 my $module = "TEST";
1026 my $patron = $builder->build_object(
1028 class => 'Koha::Patrons',
1029 value => { surname => "with_punctuation_" }
1032 my $biblio = $builder->build_object(
1033 { class => 'Koha::Biblios', value => { title => "with_punctuation_" } }
1035 my $biblioitem = $builder->build_object(
1037 class => 'Koha::Biblioitems',
1039 biblionumber => $biblio->biblionumber,
1040 isbn => "with_punctuation_"
1045 my $template = q|patron=[% borrower.surname %];biblio=[% biblio.title %];biblioitems=[% biblioitem.isbn %]|;
1046 reset_template( { template => $template, code => $code, module => $module } );
1047 my $letter = GetPreparedLetter(
1049 letter_code => $code,
1051 borrowers => $patron->borrowernumber,
1052 biblio => $biblio->biblionumber,
1053 biblioitems => $biblioitem->biblioitemnumber
1056 my $expected_letter = q|patron=with_punctuation_;biblio=with_punctuation;biblioitems=with_punctuation|;
1057 is( $letter->{content}, $expected_letter, "Pre-processing should call TT plugin to remove punctuation if table is biblio or biblioitems");
1061 sub reset_template {
1062 my ( $params ) = @_;
1063 my $template = $params->{template};
1064 my $code = $params->{code};
1065 my $module = $params->{module} || 'test_module';
1067 Koha::Notice::Templates->search( { code => $code } )->delete;
1068 Koha::Notice::Template->new(
1075 message_transport_type => 'email',
1076 content => $template
1081 sub process_letter {
1083 my $template = $params->{template};
1084 my $tables = $params->{tables};
1085 my $substitute = $params->{substitute};
1086 my $code = $params->{code};
1087 my $module = $params->{module} || 'test_module';
1088 my $branchcode = $params->{branchcode};
1090 reset_template( $params );
1092 my $letter = C4::Letters::GetPreparedLetter(
1094 letter_code => $code,
1097 substitute => $substitute,