[Patches] [PATCH] bug_7001: Issue and Reserve slips are notices.

koha-patchbot at kohaaloha.com koha-patchbot at kohaaloha.com
Fri Nov 4 19:53:21 NZDT 2011


From: Srdjan Jankovic <srdjan at catalyst.net.nz>
Date: Tue, 25 Oct 2011 13:43:03 +1300
Subject: [PATCH] bug_7001: Issue and Reserve slips are notices.

Branches can have their own version of notices - added branchcode to
letter table.
Support html notices - added is_html to letter table.
GetPreparedletter() is the interface for compiling letters (notices).
Sysprefs for notice and slips stylesheets.
---
 C4/Circulation.pm                                  |   20 +-
 C4/Letters.pm                                      |  580 +++++++++++++-------
 C4/Members.pm                                      |   81 +++-
 C4/Message.pm                                      |   12 +-
 C4/Print.pm                                        |  139 ++----
 C4/Reserves.pm                                     |  103 +++--
 C4/Suggestions.pm                                  |   22 +-
 acqui/booksellers.pl                               |    3 +-
 circ/circulation.pl                                |    3 +-
 circ/hold-transfer-slip.pl                         |   27 +-
 .../data/mysql/de-DE/mandatory/sample_notices.sql  |    2 +-
 .../data/mysql/en/mandatory/sample_notices.sql     |   84 +++-
 .../data/mysql/es-ES/mandatory/sample_notices.sql  |    2 +-
 .../mysql/fr-FR/1-Obligatoire/sample_notices.sql   |    2 +-
 installer/data/mysql/it-IT/necessari/notices.sql   |    2 +-
 installer/data/mysql/kohastructure.sql             |    4 +-
 .../mysql/nb-NO/1-Obligatorisk/sample_notices.sql  |    2 +-
 .../data/mysql/pl-PL/mandatory/sample_notices.sql  |    2 +-
 .../data/mysql/ru-RU/mandatory/sample_notices.sql  |    2 +-
 installer/data/mysql/sysprefs.sql                  |    3 +
 .../data/mysql/uk-UA/mandatory/sample_notices.sql  |    2 +-
 installer/data/mysql/updatedatabase.pl             |   98 ++++-
 .../prog/en/includes/circ-toolbar.inc              |    4 +-
 .../en/modules/admin/preferences/circulation.pref  |    5 +
 .../en/modules/admin/preferences/staff_client.pref |    5 +
 .../prog/en/modules/batch/print-notices.tt         |    6 +-
 .../prog/en/modules/circ/hold-transfer-slip.tt     |   54 --
 .../prog/en/modules/circ/printslip.tt              |   28 +
 .../prog/en/modules/members/moremember-receipt.tt  |   76 ---
 .../intranet-tmpl/prog/en/modules/tools/letter.tt  |  113 +++-
 members/memberentry.pl                             |    5 +-
 members/moremember.pl                              |   10 -
 members/printslip.pl                               |   89 +++
 misc/cronjobs/advance_notices.pl                   |   78 ++--
 misc/cronjobs/gather_print_notices.pl              |   14 +-
 misc/cronjobs/overdue_notices.pl                   |   86 ++--
 t/db_dependent/lib/KohaTest/Letters.pm             |    5 +-
 t/db_dependent/lib/KohaTest/Letters/GetLetter.pm   |    3 +-
 t/db_dependent/lib/KohaTest/Members.pm             |    1 +
 t/db_dependent/lib/KohaTest/Print.pm               |    5 +-
 t/db_dependent/lib/KohaTest/Reserves.pm            |    1 +
 tools/letter.pl                                    |  214 +++++---
 42 files changed, 1259 insertions(+), 738 deletions(-)
 delete mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/circ/hold-transfer-slip.tt
 create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/circ/printslip.tt
 delete mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember-receipt.tt
 create mode 100755 members/printslip.pl

diff --git a/C4/Circulation.pm b/C4/Circulation.pm
index 9f81773..33e5430 100644
--- a/C4/Circulation.pm
+++ b/C4/Circulation.pm
@@ -2628,11 +2628,18 @@ sub SendCirculationAlert {
         borrowernumber => $borrower->{borrowernumber},
         message_name   => $message_name{$type},
     });
-    my $letter = C4::Letters::getletter('circulation', $type);
-    C4::Letters::parseletter($letter, 'biblio',      $item->{biblionumber});
-    C4::Letters::parseletter($letter, 'biblioitems', $item->{biblionumber});
-    C4::Letters::parseletter($letter, 'borrowers',   $borrower->{borrowernumber});
-    C4::Letters::parseletter($letter, 'branches',    $branch);
+    my $letter =  C4::Letters::GetPreparedLetter (
+        module => 'circulation',
+        letter_code => $type,
+        branchcode => $branch,
+        tables => {
+            'biblio'      => $item->{biblionumber},
+            'biblioitems' => $item->{biblionumber},
+            'borrowers'   => $borrower,
+            'branches'    => $branch,
+        }
+    ) or return;
+
     my @transports = @{ $borrower_preferences->{transports} };
     # warn "no transports" unless @transports;
     for (@transports) {
@@ -2647,7 +2654,8 @@ sub SendCirculationAlert {
             $message->update;
         }
     }
-    $letter;
+
+    return $letter;
 }
 
 =head2 updateWrongTransfer
diff --git a/C4/Letters.pm b/C4/Letters.pm
index 6846a00..8ca260f 100644
--- a/C4/Letters.pm
+++ b/C4/Letters.pm
@@ -26,6 +26,7 @@ use Encode;
 use Carp;
 
 use C4::Members;
+use C4::Members::Attributes qw(GetBorrowerAttributes);
 use C4::Branch;
 use C4::Log;
 use C4::SMS;
@@ -42,7 +43,7 @@ BEGIN {
 	$VERSION = 3.01;
 	@ISA = qw(Exporter);
 	@EXPORT = qw(
-	&GetLetters &getletter &addalert &getalert &delalert &findrelatedto &SendAlerts GetPrintMessages
+	&GetLetters &GetPreparedLetter &GetWrappedLetter &addalert &getalert &delalert &findrelatedto &SendAlerts &GetPrintMessages
 	);
 }
 
@@ -117,13 +118,26 @@ sub GetLetters (;$) {
     return \%letters;
 }
 
-sub getletter ($$) {
-    my ( $module, $code ) = @_;
+my %letter;
+sub getletter ($$$) {
+    my ( $module, $code, $branchcode ) = @_;
+
+    if (C4::Context->preference('IndependantBranches') && $branchcode){
+        $$branchcode = C4::Context->userenv->{'branch'};
+    }
+
+    if ( my $l = $letter{$module}{$code}{$branchcode} ) {
+        return { %$l }; # deep copy
+    }
+
     my $dbh = C4::Context->dbh;
-    my $sth = $dbh->prepare("select * from letter where module=? and code=?");
-    $sth->execute( $module, $code );
-    my $line = $sth->fetchrow_hashref;
-    return $line;
+    my $sth = $dbh->prepare("select * from letter where module=? and code=? and (branchcode = ? or branchcode = '') order by branchcode desc limit 1");
+    $sth->execute( $module, $code, $branchcode );
+    my $line = $sth->fetchrow_hashref
+      or return;
+    $line->{'content-type'} = 'text/html; charset="UTF-8"' if $line->{is_html};
+    $letter{$module}{$code}{$branchcode} = $line;
+    return { %$line };
 }
 
 =head2 addalert ($borrowernumber, $type, $externalid)
@@ -178,7 +192,7 @@ sub delalert ($) {
 sub getalert (;$$$) {
     my ( $borrowernumber, $type, $externalid ) = @_;
     my $dbh   = C4::Context->dbh;
-    my $query = "SELECT * FROM alert WHERE";
+    my $query = "SELECT a.*, b.branchcode FROM alert a JOIN borrowers b USING(borrowernumber) WHERE";
     my @bind;
     if ($borrowernumber and $borrowernumber =~ /^\d+$/) {
         $query .= " borrowernumber=? AND ";
@@ -234,70 +248,65 @@ sub findrelatedto ($$) {
     parameters :
     - $type : the type of alert
     - $externalid : the id of the "object" to query
-    - $letter : the letter to send.
+    - $letter_code : the letter to send.
 
     send an alert to all borrowers having put an alert on a given subject.
 
 =cut
 
 sub SendAlerts {
-    my ( $type, $externalid, $letter ) = @_;
+    my ( $type, $externalid, $letter_code ) = @_;
     my $dbh = C4::Context->dbh;
     if ( $type eq 'issue' ) {
 
-        # 		warn "sending issues...";
-        my $letter = getletter( 'serial', $letter );
-
         # prepare the letter...
         # search the biblionumber
         my $sth =
           $dbh->prepare(
             "SELECT biblionumber FROM subscription WHERE subscriptionid=?");
         $sth->execute($externalid);
-        my ($biblionumber) = $sth->fetchrow;
-
-        # parsing branch info
-        my $userenv = C4::Context->userenv;
-        parseletter( $letter, 'branches', $userenv->{branch} );
-
-        # parsing librarian name
-        $letter->{content} =~ s/<<LibrarianFirstname>>/$userenv->{firstname}/g;
-        $letter->{content} =~ s/<<LibrarianSurname>>/$userenv->{surname}/g;
-        $letter->{content} =~
-          s/<<LibrarianEmailaddress>>/$userenv->{emailaddress}/g;
-
-        # parsing biblio information
-        parseletter( $letter, 'biblio',      $biblionumber );
-        parseletter( $letter, 'biblioitems', $biblionumber );
+        my ($biblionumber) = $sth->fetchrow
+          or warn( "No subscription for '$externalid'" ),
+             return;
 
+        my %letter;
         # find the list of borrowers to alert
         my $alerts = getalert( '', 'issue', $externalid );
         foreach (@$alerts) {
 
-            # and parse borrower ...
-            my $innerletter = $letter;
             my $borinfo = C4::Members::GetMember('borrowernumber' => $_->{'borrowernumber'});
-            parseletter( $innerletter, 'borrowers', $_->{'borrowernumber'} );
+            my $email = $borinfo->{email} or next;
+
+            # 		warn "sending issues...";
+            my $userenv = C4::Context->userenv;
+            my $letter = GetPreparedLetter (
+                module => 'serial',
+                letter_code => $letter_code,
+                branchcode => $userenv->{branch},
+                tables => {
+                    'branches'    => $_->{branchcode},
+                    'biblio'      => $biblionumber,
+                    'biblioitems' => $biblionumber,
+                    'borrowers'   => $borinfo,
+                },
+                want_librarian => 1,
+            ) or return;
 
             # ... then send mail
-            if ( $borinfo->{email} ) {
-                my %mail = (
-                    To      => $borinfo->{email},
-                    From    => $borinfo->{email},
-                    Subject => "" . $innerletter->{title},
-                    Message => "" . $innerletter->{content},
-                    'Content-Type' => 'text/plain; charset="utf8"',
-                    );
-                sendmail(%mail) or carp $Mail::Sendmail::error;
-
+            my %mail = (
+                To      => $email,
+                From    => $email,
+                Subject => "" . $letter->{title},
+                Message => "" . $letter->{content},
+                'Content-Type' => 'text/plain; charset="utf8"',
+                );
+            sendmail(%mail) or carp $Mail::Sendmail::error;
 # warn "sending to $mail{To} From $mail{From} subj $mail{Subject} Mess $mail{Message}";
-            }
         }
     }
     elsif ( $type eq 'claimacquisition' ) {
 
         # 		warn "sending issues...";
-        my $letter = getletter( 'claimacquisition', $letter );
 
         # prepare the letter...
         # search the biblionumber
@@ -307,52 +316,43 @@ sub SendAlerts {
         my $sthorders = $dbh->prepare($strsth);
         $sthorders->execute;
         my $dataorders = $sthorders->fetchall_arrayref( {} );
-        parseletter( $letter, 'aqbooksellers',
-            $dataorders->[0]->{booksellerid} );
+
         my $sthbookseller =
           $dbh->prepare("select * from aqbooksellers where id=?");
         $sthbookseller->execute( $dataorders->[0]->{booksellerid} );
         my $databookseller = $sthbookseller->fetchrow_hashref;
 
-        # parsing branch info
-        my $userenv = C4::Context->userenv;
-        parseletter( $letter, 'branches', $userenv->{branch} );
-
-        # parsing librarian name
-        $letter->{content} =~ s/<<LibrarianFirstname>>/$userenv->{firstname}/g;
-        $letter->{content} =~ s/<<LibrarianSurname>>/$userenv->{surname}/g;
-        $letter->{content} =~
-          s/<<LibrarianEmailaddress>>/$userenv->{emailaddress}/g;
-        foreach my $data ( @{$dataorders} ) {
-            if ( $letter->{content} =~ m/(<<.*>>)/ ) {
-                my $line = $1;
-                foreach my $field ( keys %{$data} ) {
-                    $line =~ s/(<<[^\.]+.$field>>)/$data->{$field}/;
-                }
-                $letter->{content} =~ s/(<<.*>>)/$line\n$1/;
-            }
+        my @email;
+        push @email, $databookseller->{bookselleremail} if $databookseller->{bookselleremail};
+        push @email, $databookseller->{contemail}       if $databookseller->{contemail};
+        unless (@email) {
+            warn "Bookseller $dataorders->[0]->{booksellerid} without emails";
+            return;
         }
-        $letter->{content} =~ s/<<[^>]*>>//g;
-        my $innerletter = $letter;
+
+        my $userenv = C4::Context->userenv;
+        my $letter = GetPreparedLetter (
+            module => 'claimacquisition',
+            letter_code => $letter_code,
+            branchcode => $userenv->{branch},
+            tables => {
+                'branches'    => $userenv->{branch},
+                'aqbooksellers' => $databookseller,
+            },
+            repeat => $dataorders,
+            want_librarian => 1,
+        ) or return;
 
         # ... then send mail
-        if (   $databookseller->{bookselleremail}
-            || $databookseller->{contemail} )
-        {
-            my %mail = (
-                To => $databookseller->{bookselleremail}
-                  . (
-                    $databookseller->{contemail}
-                    ? "," . $databookseller->{contemail}
-                    : ""
-                  ),
-                From           => $userenv->{emailaddress},
-                Subject        => "" . $innerletter->{title},
-                Message        => "" . $innerletter->{content},
-                'Content-Type' => 'text/plain; charset="utf8"',
-            );
-            sendmail(%mail) or carp $Mail::Sendmail::error;
-        }
+        my %mail = (
+            To => join( ','. @email),
+            From           => $userenv->{emailaddress},
+            Subject        => "" . $letter->{title},
+            Message        => "" . $letter->{content},
+            'Content-Type' => 'text/plain; charset="utf8"',
+        );
+        sendmail(%mail) or carp $Mail::Sendmail::error;
+
         if ( C4::Context->preference("LetterLog") ) {
             logaction(
                 "ACQUISITION",
@@ -360,16 +360,13 @@ sub SendAlerts {
                 "",
                 "order list : "
                   . join( ",", @$externalid )
-                  . "\n$innerletter->{title}\n$innerletter->{content}"
+                  . "\n$letter->{title}\n$letter->{content}"
             );
         }
     }
     elsif ( $type eq 'claimissues' ) {
 
         # 		warn "sending issues...";
-        my $letter = getletter( 'claimissues', $letter );
-
-        # prepare the letter...
         # search the biblionumber
         my $strsth =
 "select serial.*,subscription.*, biblio.* from serial LEFT JOIN subscription on serial.subscriptionid=subscription.subscriptionid LEFT JOIN biblio on serial.biblionumber=biblio.biblionumber where serial.serialid IN ("
@@ -377,81 +374,76 @@ sub SendAlerts {
         my $sthorders = $dbh->prepare($strsth);
         $sthorders->execute;
         my $dataorders = $sthorders->fetchall_arrayref( {} );
-        parseletter( $letter, 'aqbooksellers',
-            $dataorders->[0]->{aqbooksellerid} );
+
         my $sthbookseller =
           $dbh->prepare("select * from aqbooksellers where id=?");
         $sthbookseller->execute( $dataorders->[0]->{aqbooksellerid} );
         my $databookseller = $sthbookseller->fetchrow_hashref;
 
-        # parsing branch info
-        my $userenv = C4::Context->userenv;
-        parseletter( $letter, 'branches', $userenv->{branch} );
-
-        # parsing librarian name
-        $letter->{content} =~ s/<<LibrarianFirstname>>/$userenv->{firstname}/g;
-        $letter->{content} =~ s/<<LibrarianSurname>>/$userenv->{surname}/g;
-        $letter->{content} =~
-          s/<<LibrarianEmailaddress>>/$userenv->{emailaddress}/g;
-        foreach my $data ( @{$dataorders} ) {
-            if ( $letter->{content} =~ m/(<<.*>>)/ ) {
-                my $line = $1;
-                foreach my $field ( keys %{$data} ) {
-                    $line =~ s/(<<[^\.]+.$field>>)/$data->{$field}/;
-                }
-                $letter->{content} =~ s/(<<.*>>)/$line\n$1/;
-            }
+        my @email;
+        push @email, $databookseller->{bookselleremail} if $databookseller->{bookselleremail};
+        push @email, $databookseller->{contemail}       if $databookseller->{contemail};
+        unless (@email) {
+            warn "Bookseller $dataorders->[0]->{booksellerid} without emails";
+            return;
         }
-        $letter->{content} =~ s/<<[^>]*>>//g;
-        my $innerletter = $letter;
+
+        # prepare the letter...
+        my $userenv = C4::Context->userenv;
+        my $letter = GetPreparedLetter (
+            module => 'claimissues',
+            letter_code => $letter_code,
+            branchcode => $userenv->{branch},
+            tables => {
+                'branches'    => $userenv->{branch},
+                'aqbooksellers' => $databookseller,
+            },
+            repeat => $dataorders,
+            want_librarian => 1,
+        ) or return;
 
         # ... then send mail
-        if (   $databookseller->{bookselleremail}
-            || $databookseller->{contemail} ) {
-            my $mail_to = $databookseller->{bookselleremail};
-            if ($databookseller->{contemail}) {
-                if (!$mail_to) {
-                    $mail_to = $databookseller->{contemail};
-                } else {
-                    $mail_to .= q|,|;
-                    $mail_to .= $databookseller->{contemail};
-                }
-            }
-            my $mail_subj = $innerletter->{title};
-            my $mail_msg  = $innerletter->{content};
-            $mail_msg  ||= q{};
-            $mail_subj ||= q{};
+        my $mail_subj = $letter->{title};
+        my $mail_msg  = $letter->{content};
+        $mail_msg  ||= q{};
+        $mail_subj ||= q{};
 
-            my %mail = (
-                To => $mail_to,
-                From    => $userenv->{emailaddress},
-                Subject => $mail_subj,
-                Message => $mail_msg,
-                'Content-Type' => 'text/plain; charset="utf8"',
-            );
-            sendmail(%mail) or carp $Mail::Sendmail::error;
-            logaction(
-                "ACQUISITION",
-                "CLAIM ISSUE",
-                undef,
-                "To="
-                  . $databookseller->{contemail}
-                  . " Title="
-                  . $innerletter->{title}
-                  . " Content="
-                  . $innerletter->{content}
-            ) if C4::Context->preference("LetterLog");
-        }
+        my %mail = (
+            To => join( ','. @email),
+            From    => $userenv->{emailaddress},
+            Subject => $mail_subj,
+            Message => $mail_msg,
+            'Content-Type' => 'text/plain; charset="utf8"',
+        );
+        sendmail(%mail) or carp $Mail::Sendmail::error;
+
+        logaction(
+            "ACQUISITION",
+            "CLAIM ISSUE",
+            undef,
+            "To="
+                . $databookseller->{contemail}
+                . " Title="
+                . $letter->{title}
+                . " Content="
+                . $letter->{content}
+        ) if C4::Context->preference("LetterLog");
     }    
    # send an "account details" notice to a newly created user 
     elsif ( $type eq 'members' ) {
-        # must parse the password special, before it's hashed.
-        $letter->{content} =~ s/<<borrowers.password>>/$externalid->{'password'}/g;
-
-        parseletter( $letter, 'borrowers', $externalid->{'borrowernumber'});
-        parseletter( $letter, 'branches', $externalid->{'branchcode'} );
-
         my $branchdetails = GetBranchDetail($externalid->{'branchcode'});
+        my $letter = GetPreparedLetter (
+            module => 'members',
+            letter_code => $letter_code,
+            branchcode => $externalid->{'branchcode'},
+            tables => {
+                'branches'    => $branchdetails,
+                'borrowers' => $externalid->{'borrowernumber'},
+            },
+            substitute => { 'borrowers.password' => $externalid->{'password'} },
+            want_librarian => 1,
+        ) or return;
+
         my %mail = (
                 To      =>     $externalid->{'emailaddr'},
                 From    =>  $branchdetails->{'branchemail'} || C4::Context->preference("KohaAdminEmailAddress"),
@@ -463,24 +455,148 @@ sub SendAlerts {
     }
 }
 
-=head2 parseletter($letter, $table, $pk)
-
-    parameters :
-    - $letter : a hash to letter fields (title & content useful)
-    - $table : the Koha table to parse.
-    - $pk : the primary key to query on the $table table
-    parse all fields from a table, and replace values in title & content with the appropriate value
-    (not exported sub, used only internally)
+=head2 GetPreparedLetter( %params )
+
+    %params hash:
+      module => letter module, mandatory
+      letter_code => letter code, mandatory
+      branchcode => for letter selection, if missing default system letter taken
+      tables => a hashref with table names as keys. Values are either:
+        - a scalar - primary key value
+        - an arrayref - primary key values
+        - a hashref - full record
+      substitute => custom substitution key/value pairs
+      repeat => records to be substituted on consecutive lines:
+        - an arrayref - tries to guess what needs substituting by
+          taking remaining << >> tokensr; not recommended
+        - a hashref token => @tables - replaces <token> << >> << >> </token>
+          subtemplate for each @tables row; table is a hashref as above
+      want_librarian => boolean,  if set to true triggers librarian details
+        substitution from the userenv
+    Return value:
+      letter fields hashref (title & content useful)
 
 =cut
 
-our %handles = ();
-our %columns = ();
+sub GetPreparedLetter {
+    my %params = @_;
+
+    my $module      = $params{module} or croak "No module";
+    my $letter_code = $params{letter_code} or croak "No letter_code";
+    my $branchcode  = $params{branchcode} || '';
+
+    my $letter = getletter( $module, $letter_code, $branchcode )
+        or warn( "No $module $letter_code letter"),
+            return;
+
+    my $tables = $params{tables};
+    my $substitute = $params{substitute};
+    my $repeat = $params{repeat};
+    $tables || $substitute || $repeat
+      or carp( "ERROR: nothing to substitute - both 'tables' and 'substitute' are empty" ),
+         return;
+    my $want_librarian = $params{want_librarian};
+
+    if ($substitute) {
+        while ( my ($token, $val) = each %$substitute ) {
+            $letter->{title} =~ s/<<$token>>/$val/g;
+            $letter->{content} =~ s/<<$token>>/$val/g;
+       }
+    }
+
+    if ($want_librarian) {
+        # parsing librarian name
+        my $userenv = C4::Context->userenv;
+        $letter->{content} =~ s/<<LibrarianFirstname>>/$userenv->{firstname}/go;
+        $letter->{content} =~ s/<<LibrarianSurname>>/$userenv->{surname}/go;
+        $letter->{content} =~ s/<<LibrarianEmailaddress>>/$userenv->{emailaddress}/go;
+    }
+
+    my ($repeat_no_enclosing_tags, $repeat_enclosing_tags);
+
+    if ($repeat) {
+        if (ref ($repeat) eq 'ARRAY' ) {
+            $repeat_no_enclosing_tags = $repeat;
+        } else {
+            $repeat_enclosing_tags = $repeat;
+        }
+    }
+
+    if ($repeat_enclosing_tags) {
+        while ( my ($tag, $tag_tables) = each %$repeat_enclosing_tags ) {
+            if ( $letter->{content} =~ m!<$tag>(.*)</$tag>!s ) {
+                my $subcontent = $1;
+                my @lines = map {
+                    my %subletter = ( title => '', content => $subcontent );
+                    _substitute_tables( \%subletter, $_ );
+                    $subletter{content};
+                } @$tag_tables;
+                $letter->{content} =~ s!<$tag>.*</$tag>!join( "\n", @lines )!se;
+            }
+        }
+    }
+
+    if ($tables) {
+        _substitute_tables( $letter, $tables );
+    }
+
+    if ($repeat_no_enclosing_tags) {
+        if ( $letter->{content} =~ m/[^\n]*<<.*>>[^\n]*/so ) {
+            my $line = $&;
+            my $i = 1;
+            my @lines = map {
+                my $c = $line;
+                $c =~ s/<<count>>/$i/go;
+                foreach my $field ( keys %{$_} ) {
+                    $c =~ s/(<<[^\.]+.$field>>)/$_->{$field}/;
+                }
+                $i++;
+                $c;
+            } @$repeat_no_enclosing_tags;
+
+            my $replaceby = join( "\n", @lines );
+            $letter->{content} =~ s/\Q$line\E/$replaceby/s;
+        }
+    }
+
+    $letter->{content} =~ s/<<\S*>>//go; #remove any stragglers
+#   $letter->{content} =~ s/<<[^>]*>>//go;
+
+    return $letter;
+}
+
+sub _substitute_tables {
+    my ( $letter, $tables ) = @_;
+    while ( my ($table, $param) = each %$tables ) {
+        next unless $param;
+
+        my $ref = ref $param;
 
-sub parseletter_sth {
+        my $values;
+        if ($ref && $ref eq 'HASH') {
+            $values = $param;
+        }
+        else {
+            my @pk;
+            my $sth = _parseletter_sth($table);
+            unless ($sth) {
+                warn "_parseletter_sth('$table') failed to return a valid sth.  No substitution will be done for that table.";
+                return;
+            }
+            $sth->execute( $ref ? @$param : $param );
+
+            $values = $sth->fetchrow_hashref;
+        }
+
+        _parseletter ( $letter, $table, $values );
+    }
+}
+
+my %handles = ();
+sub _parseletter_sth {
     my $table = shift;
     unless ($table) {
-        carp "ERROR: parseletter_sth() called without argument (table)";
+        carp "ERROR: _parseletter_sth() called without argument (table)";
         return;
     }
     # check cache first
@@ -496,8 +612,10 @@ sub parseletter_sth {
     ($table eq 'branches'     ) ? "SELECT * FROM $table WHERE     branchcode = ?"                      :
     ($table eq 'suggestions'  ) ? "SELECT * FROM $table WHERE   suggestionid = ?"                      :
     ($table eq 'aqbooksellers') ? "SELECT * FROM $table WHERE             id = ?"                      : undef ;
+    ($table eq 'aqorders'     ) ? "SELECT * FROM $table WHERE    ordernumber = ?"                      : undef ;
+    ($table eq 'opac_news'    ) ? "SELECT * FROM $table WHERE          idnew = ?"                      : undef ;
     unless ($query) {
-        warn "ERROR: No parseletter_sth query for table '$table'";
+        warn "ERROR: No _parseletter_sth query for table '$table'";
         return;     # nothing to get
     }
     unless ($handles{$table} = C4::Context->dbh->prepare($query)) {
@@ -507,25 +625,21 @@ sub parseletter_sth {
     return $handles{$table};    # now cache is populated for that $table
 }
 
-sub parseletter {
-    my ( $letter, $table, $pk, $pk2 ) = @_;
-    unless ($letter) {
-        carp "ERROR: parseletter() 1st argument 'letter' empty";
-        return;
-    }
-    my $sth = parseletter_sth($table);
-    unless ($sth) {
-        warn "parseletter_sth('$table') failed to return a valid sth.  No substitution will be done for that table.";
-        return;
-    }
-    if ( $pk2 ) {
-        $sth->execute($pk, $pk2);
-    } else {
-        $sth->execute($pk);
-    }
+=head2 _parseletter($letter, $table, $values)
 
-    my $values = $sth->fetchrow_hashref;
-    
+    parameters :
+    - $letter : a hash to letter fields (title & content useful)
+    - $table : the Koha table to parse.
+    - $values : table record hashref
+    parse all fields from a table, and replace values in title & content with the appropriate value
+    (not exported sub, used only internally)
+
+=cut
+
+my %columns = ();
+sub _parseletter {
+    my ( $letter, $table, $values ) = @_;
+   
     # TEMPORARY hack until the expirationdate column is added to reserves
     if ( $table eq 'reserves' && $values->{'waitingdate'} ) {
         my @waitingdate = split /-/, $values->{'waitingdate'};
@@ -539,16 +653,51 @@ sub parseletter {
         )->output();
     }
 
+    if ($letter->{content} && $letter->{content} =~ /<<today>>/) {
+        my @da = localtime();
+        my $todaysdate = "$da[2]:$da[1]  " . C4::Dates->today();
+        $letter->{content} =~ s/<<today>>/$todaysdate/go;
+    }
 
     # and get all fields from the table
-    my $columns = C4::Context->dbh->prepare("SHOW COLUMNS FROM $table");
-    $columns->execute;
-    while ( ( my $field ) = $columns->fetchrow_array ) {
-        my $replacefield = "<<$table.$field>>";
-        $values->{$field} =~ s/\p{P}(?=$)//g if $values->{$field};
-        my $replacedby   = $values->{$field} || '';
-        ($letter->{title}  ) and $letter->{title}   =~ s/$replacefield/$replacedby/g;
-        ($letter->{content}) and $letter->{content} =~ s/$replacefield/$replacedby/g;
+#   my $columns = $columns{$table};
+#   unless ($columns) {
+#       $columns = $columns{$table} =  C4::Context->dbh->selectcol_arrayref("SHOW COLUMNS FROM $table");
+#   }
+#   foreach my $field (@$columns) {
+
+    while ( my ($field, $val) = each %$values ) {
+        my $replacetablefield = "<<$table.$field>>";
+        my $replacefield = "<<$field>>";
+        $val =~ s/\p{P}(?=$)//g if $val;
+        my $replacedby   = defined ($val) ? $val : '';
+        ($letter->{title}  ) and do {
+            $letter->{title}   =~ s/$replacetablefield/$replacedby/g;
+            $letter->{title}   =~ s/$replacefield/$replacedby/g;
+        };
+        ($letter->{content}) and do {
+            $letter->{content} =~ s/$replacetablefield/$replacedby/g;
+            $letter->{content} =~ s/$replacefield/$replacedby/g;
+        };
+    }
+
+    if ($table eq 'borrowers' && $letter->{content}) {
+        if ( my $attributes = GetBorrowerAttributes($values->{borrowernumber}) ) {
+            my %attr;
+            foreach (@$attributes) {
+                my $code = $_->{code};
+                my $val  = $_->{value_description} || $_->{value};
+                $val =~ s/\p{P}(?=$)//g if $val;
+                next unless $val gt '';
+                $attr{$code} ||= [];
+                push @{ $attr{$code} }, $val;
+            }
+            while ( my ($code, $val_ar) = each %attr ) {
+                my $replacefield = "<<borrower-attribute:$code>>";
+                my $replacedby   = join ',', @$val_ar;
+                $letter->{content} =~ s/$replacefield/$replacedby/g;
+            }
+        }
     }
     return $letter;
 }
@@ -733,31 +882,32 @@ returns your letter object, with the content updated.
 sub _add_attachments {
     my $params = shift;
 
-    return unless 'HASH' eq ref $params;
-    foreach my $required_parameter (qw( letter attachments message )) {
-        return unless exists $params->{$required_parameter};
-    }
-    return $params->{'letter'} unless @{ $params->{'attachments'} };
+    my $letter = $params->{'letter'};
+    my $attachments = $params->{'attachments'};
+    return $letter unless @$attachments;
+    my $message = $params->{'message'};
 
     # First, we have to put the body in as the first attachment
-    $params->{'message'}->attach(
-        Type => 'TEXT',
-        Data => $params->{'letter'}->{'content'},
+    $message->attach(
+        Type => $letter->{'content-type'} || 'TEXT',
+        Data => $letter->{'is_html'}
+            ? _wrap_html($letter->{'content'}, $letter->{'title'})
+            : $letter->{'content'},
     );
 
-    foreach my $attachment ( @{ $params->{'attachments'} } ) {
-        $params->{'message'}->attach(
+    foreach my $attachment ( @$attachments ) {
+        $message->attach(
             Type     => $attachment->{'type'},
             Data     => $attachment->{'content'},
             Filename => $attachment->{'filename'},
         );
     }
     # we're forcing list context here to get the header, not the count back from grep.
-    ( $params->{'letter'}->{'content-type'} ) = grep( /^Content-Type:/, split( /\n/, $params->{'message'}->header_as_string ) );
-    $params->{'letter'}->{'content-type'} =~ s/^Content-Type:\s+//;
-    $params->{'letter'}->{'content'} = $params->{'message'}->body_as_string;
+    ( $letter->{'content-type'} ) = grep( /^Content-Type:/, split( /\n/, $params->{'message'}->header_as_string ) );
+    $letter->{'content-type'} =~ s/^Content-Type:\s+//;
+    $letter->{'content'} = $message->body_as_string;
 
-    return $params->{'letter'};
+    return $letter;
 
 }
 
@@ -824,14 +974,17 @@ sub _send_message_by_email ($;$$$) {
 
     my $utf8   = decode('MIME-Header', $message->{'subject'} );
     $message->{subject}= encode('MIME-Header', $utf8);
+    my $subject = encode('utf8', $message->{'subject'});
     my $content = encode('utf8', $message->{'content'});
+    my $content_type = $message->{'content_type'} || 'text/plain; charset="UTF-8"';
+    my $is_html = $content_type =~ m/html/io;
     my %sendmail_params = (
         To   => $to_address,
         From => $message->{'from_address'} || C4::Context->preference('KohaAdminEmailAddress'),
-        Subject => encode('utf8', $message->{'subject'}),
+        Subject => $subject,
         charset => 'utf8',
-        Message => $content,
-        'content-type' => $message->{'content_type'} || 'text/plain; charset="UTF-8"',
+        Message => $is_html ? _wrap_html($content, $subject) : $content,
+        'content-type' => $content_type,
     );
     $sendmail_params{'Auth'} = {user => $username, pass => $password, method => $method} if $username;
     if ( my $bcc = C4::Context->preference('OverdueNoticeBcc') ) {
@@ -851,6 +1004,27 @@ sub _send_message_by_email ($;$$$) {
     }
 }
 
+sub _wrap_html {
+    my ($content, $title) = @_;
+
+    my $css = C4::Context->preference("NoticeCSS") || '';
+    $css = qq{<link rel="stylesheet" type="text/css" href="$css">} if $css;
+    return <<EOS;
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>$title</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+$css
+</head>
+<body>
+$content
+</body>
+</html>
+EOS
+}
+
 sub _send_message_by_sms ($) {
     my $message = shift or return undef;
     my $member = C4::Members::GetMember( 'borrowernumber' => $message->{'borrowernumber'} );
diff --git a/C4/Members.pm b/C4/Members.pm
index 56718f0..2abc178 100644
--- a/C4/Members.pm
+++ b/C4/Members.pm
@@ -23,7 +23,7 @@ package C4::Members;
 use strict;
 #use warnings; FIXME - Bug 2505
 use C4::Context;
-use C4::Dates qw(format_date_in_iso);
+use C4::Dates qw(format_date_in_iso format_date);
 use Digest::MD5 qw(md5_base64);
 use Date::Calc qw/Today Add_Delta_YM/;
 use C4::Log; # logaction
@@ -31,8 +31,10 @@ use C4::Overdues;
 use C4::Reserves;
 use C4::Accounts;
 use C4::Biblio;
+use C4::Letters;
 use C4::SQLHelper qw(InsertInTable UpdateInTable SearchInTable);
 use C4::Members::Attributes qw(SearchIdMatchingAttribute);
+use C4::NewsChannels; #get slip news
 
 our ($VERSION, at ISA, at EXPORT, at EXPORT_OK,$debug);
 
@@ -91,6 +93,8 @@ BEGIN {
 		&DeleteMessage
 		&GetMessages
 		&GetMessagesCount
+
+        &IssueSlip
 	);
 
 	#Modify data
@@ -2243,7 +2247,80 @@ sub DeleteMessage {
 
 }
 
-END { }    # module clean-up code here (global destructor)
+=head2 IssueSlip
+
+  IssueSlip($branchcode, $borrowernumber, $quickslip)
+
+  Returns letter hash ( see C4::Letters::GetPreparedLetter )
+
+  $quickslip is boolean, to indicate whether we want a quick slip
+
+=cut
+
+sub IssueSlip {
+    my ($branch, $borrowernumber, $quickslip) = @_;
+
+#   return unless ( C4::Context->boolean_preference('printcirculationslips') );
+
+    my $today       = POSIX::strftime("%Y-%m-%d", localtime);
+
+    my $issueslist = GetPendingIssues($borrowernumber);
+    foreach my $it (@$issueslist){
+        if ($it->{'issuedate'} eq $today) {
+            $it->{'today'} = 1;
+        }
+        elsif ($it->{'date_due'} le $today) {
+            $it->{'overdue'} = 1;
+        }
+
+        $it->{'date_due'}=format_date($it->{'date_due'});
+    }
+    my @issues = sort { $b->{'timestamp'} <=> $a->{'timestamp'} } @$issueslist;
+
+    my ($letter_code, %repeat);
+    if ( $quickslip ) {
+        $letter_code = 'ISSUEQSLIP';
+        %repeat =  (
+            'checkedout' => [ map {
+                'biblio' => $_,
+                'items'  => $_,
+                'issues' => $_,
+            }, grep { $_->{'today'} } @issues ],
+        );
+    }
+    else {
+        $letter_code = 'ISSUESLIP';
+        %repeat =  (
+            'checkedout' => [ map {
+                'biblio' => $_,
+                'items'  => $_,
+                'issues' => $_,
+            }, grep { !$_->{'overdue'} } @issues ],
+
+            'overdue' => [ map {
+                'biblio' => $_,
+                'items'  => $_,
+                'issues' => $_,
+            }, grep { $_->{'overdue'} } @issues ],
+
+            'news' => [ map {
+                $_->{'timestamp'} = $_->{'newdate'};
+                { opac_news => $_ }
+            } @{ GetNewsToDisplay("slip") } ],
+        );
+    }
+
+    return  C4::Letters::GetPreparedLetter (
+        module => 'circulation',
+        letter_code => $letter_code,
+        branchcode => $branch,
+        tables => {
+            'branches'    => $branch,
+            'borrowers'   => $borrowernumber,
+        },
+        repeat => \%repeat,
+    );
+}
 
 1;
 
diff --git a/C4/Message.pm b/C4/Message.pm
index 16272ff..4b88970 100644
--- a/C4/Message.pm
+++ b/C4/Message.pm
@@ -18,9 +18,15 @@ How to add a new message to the queue:
   use C4::Items;
   my $borrower = { borrowernumber => 1 };
   my $item     = C4::Items::GetItem(1);
-  my $letter   = C4::Letters::getletter('circulation', 'CHECKOUT');
-  C4::Letters::parseletter($letter, 'biblio', $item->{biblionumber});
-  C4::Letters::parseletter($letter, 'biblioitems', $item->{biblionumber});
+  my $letter =  C4::Letters::GetPreparedLetter (
+      module => 'circulation',
+      letter_code => 'CHECKOUT',
+      branchcode => $branch,
+      tables => {
+          'biblio', $item->{biblionumber},
+          'biblioitems', $item->{biblionumber},
+      },
+  );
   C4::Message->enqueue($letter, $borrower->{borrowernumber}, 'email');
 
 How to update a borrower's last checkout message:
diff --git a/C4/Print.pm b/C4/Print.pm
index 2ba7584..2343fa4 100644
--- a/C4/Print.pm
+++ b/C4/Print.pm
@@ -20,8 +20,6 @@ package C4::Print;
 use strict;
 #use warnings; FIXME - Bug 2505
 use C4::Context;
-use C4::Members;
-use C4::Dates qw(format_date);
 
 use vars qw($VERSION @ISA @EXPORT);
 
@@ -30,7 +28,7 @@ BEGIN {
 	$VERSION = 3.01;
 	require Exporter;
 	@ISA    = qw(Exporter);
-	@EXPORT = qw(&remoteprint &printreserve &printslip);
+	@EXPORT = qw(&printslip);
 }
 
 =head1 NAME
@@ -47,28 +45,48 @@ The functions in this module handle sending text to a printer.
 
 =head1 FUNCTIONS
 
-=head2 remoteprint
+=cut
 
-  &remoteprint($items, $borrower);
+=comment
+    my $slip = <<"EOF";
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Date: $todaysdate;
 
-Prints the list of items in C<$items> to a printer.
+ITEM RESERVED: 
+$itemdata->{'title'} ($itemdata->{'author'})
+barcode: $itemdata->{'barcode'}
+
+COLLECT AT: $branchname
 
-C<$borrower> is a reference-to-hash giving information about a patron.
-This may be gotten from C<&GetMemberDetails>. The patron's name
-will be printed in the output.
+BORROWER:
+$bordata->{'surname'}, $bordata->{'firstname'}
+card number: $bordata->{'cardnumber'}
+Phone: $bordata->{'phone'}
+$bordata->{'streetaddress'}
+$bordata->{'suburb'}
+$bordata->{'town'}
+$bordata->{'emailaddress'}
 
-C<$items> is a reference-to-list, where each element is a
-reference-to-hash describing a borrowed item. C<$items> may be gotten
-from C<&GetBorrowerIssues>.
 
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+EOF
 =cut
 
+=head2 printslip
+
+  &printslip($slip)
+
+print a slip for the given $borrowernumber and $branchcode
+
+=cut
+
+sub printslip ($) {
+    my ($slip) = @_;
+
+    return unless ( C4::Context->boolean_preference('printcirculationslips') );
+
 # FIXME - It'd be nifty if this could generate pretty PostScript.
-sub remoteprint ($$) {
-    my ($items, $borrower) = @_;
 
-    (return)
-      unless ( C4::Context->boolean_preference('printcirculationslips') );
     my $queue = '';
 
     # FIXME - If 'queue' is undefined or empty, then presumably it should
@@ -93,100 +111,13 @@ sub remoteprint ($$) {
 
     #  print $queue;
     #open (FILE,">/tmp/$file");
-    my $i      = 0;
-    # FIXME - This is HLT-specific. Put this stuff in a customizable
-    # site-specific file somewhere.
-    print PRINTER "Horowhenua Library Trust\r\n";
-    print PRINTER "Phone: 368-1953\r\n";
-    print PRINTER "Fax:    367-9218\r\n";
-    print PRINTER "Email:  renewals\@library.org.nz\r\n\r\n\r\n";
-    print PRINTER "$borrower->{'cardnumber'}\r\n";
-    print PRINTER
-      "$borrower->{'title'} $borrower->{'initials'} $borrower->{'surname'}\r\n";
-
-    # FIXME - Use   for ($i = 0; $items->[$i]; $i++)
-    # Or better yet,   foreach $item (@{$items})
-    while ( $items->[$i] ) {
-
-        #    print $i;
-        my $itemdata = $items->[$i];
-
-        # FIXME - This is just begging for a Perl format.
-        print PRINTER "$i $itemdata->{'title'}\r\n";
-        print PRINTER "$itemdata->{'barcode'}";
-        print PRINTER " " x 15;
-        print PRINTER "$itemdata->{'date_due'}\r\n";
-        $i++;
-    }
+    print PRINTER $slip;
     print PRINTER "\r\n" x 7 ;
     close PRINTER;
 
     #system("lpr /tmp/$file");
 }
 
-sub printreserve {
-    my ( $branchname, $bordata, $itemdata ) = @_;
-    my $printer = '';
-    (return) unless ( C4::Context->boolean_preference('printreserveslips') );
-    if ( $printer eq "" || $printer eq 'nulllp' ) {
-        open( PRINTER, ">>/tmp/kohares" )
-		  or die "Could not write to /tmp/kohares";
-    }
-    else {
-        open( PRINTER, "| lpr -P $printer >/dev/null" )
-          or die "Couldn't write to queue:$!\n";
-    }
-    my @da = localtime();
-    my $todaysdate = "$da[2]:$da[1]  " . C4::Dates->today();
-    my $slip = <<"EOF";
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Date: $todaysdate;
-
-ITEM RESERVED: 
-$itemdata->{'title'} ($itemdata->{'author'})
-barcode: $itemdata->{'barcode'}
-
-COLLECT AT: $branchname
-
-BORROWER:
-$bordata->{'surname'}, $bordata->{'firstname'}
-card number: $bordata->{'cardnumber'}
-Phone: $bordata->{'phone'}
-$bordata->{'streetaddress'}
-$bordata->{'suburb'}
-$bordata->{'town'}
-$bordata->{'emailaddress'}
-
-
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-EOF
-    print PRINTER $slip;
-    close PRINTER;
-    return $slip;
-}
-
-=head2 printslip
-
-  &printslip($borrowernumber)
-
-print a slip for the given $borrowernumber
-
-=cut
-
-#'
-sub printslip ($) {
-    my $borrowernumber = shift;
-    my $borrower   = GetMemberDetails($borrowernumber);
-	my $issueslist = GetPendingIssues($borrowernumber); 
-	foreach my $it (@$issueslist){
-		$it->{'date_due'}=format_date($it->{'date_due'});
-    }		
-    my @issues = sort { $b->{'timestamp'} <=> $a->{'timestamp'} } @$issueslist;
-    remoteprint(\@issues, $borrower );
-}
-
-END { }    # module clean-up code here (global destructor)
-
 1;
 __END__
 
diff --git a/C4/Reserves.pm b/C4/Reserves.pm
index 4af8a85..886829e 100644
--- a/C4/Reserves.pm
+++ b/C4/Reserves.pm
@@ -120,6 +120,8 @@ BEGIN {
         
         &AlterPriority
         &ToggleLowestPriority
+
+        &ReserveSlip
     );
     @EXPORT_OK = qw( MergeHolds );
 }    
@@ -193,32 +195,31 @@ sub AddReserve {
     # Send e-mail to librarian if syspref is active
     if(C4::Context->preference("emailLibrarianWhenHoldIsPlaced")){
         my $borrower = C4::Members::GetMember(borrowernumber => $borrowernumber);
-        my $biblio   = GetBiblioData($biblionumber);
-        my $letter = C4::Letters::getletter( 'reserves', 'HOLDPLACED');
-	my $branchcode = $borrower->{branchcode};
-        my $branch_details = C4::Branch::GetBranchDetail($branchcode);
-        my $admin_email_address =$branch_details->{'branchemail'} || C4::Context->preference('KohaAdminEmailAddress');
-
-        my %keys = (%$borrower, %$biblio);
-        foreach my $key (keys %keys) {
-            my $replacefield = "<<$key>>";
-            $letter->{content} =~ s/$replacefield/$keys{$key}/g;
-            $letter->{title} =~ s/$replacefield/$keys{$key}/g;
+        my $branch_details = C4::Branch::GetBranchDetail($borrower->{branchcode});
+        if ( my $letter =  C4::Letters::GetPreparedLetter (
+            module => 'reserves',
+            letter_code => 'HOLDPLACED',
+            branchcode => $branch,
+            tables => {
+                'branches'  => $branch_details,
+                'borrowers' => $borrower,
+                'biblio'    => $biblionumber,
+            },
+        ) ) {
+
+            my $admin_email_address =$branch_details->{'branchemail'} || C4::Context->preference('KohaAdminEmailAddress');
+
+            C4::Letters::EnqueueLetter(
+                {   letter                 => $letter,
+                    borrowernumber         => $borrowernumber,
+                    message_transport_type => 'email',
+                    from_address           => $admin_email_address,
+                    to_address           => $admin_email_address,
+                }
+            );
         }
-        
-        C4::Letters::EnqueueLetter(
-                            {   letter                 => $letter,
-                                borrowernumber         => $borrowernumber,
-                                message_transport_type => 'email',
-                                from_address           => $admin_email_address,
-                                to_address           => $admin_email_address,
-                            }
-                        );
-        
-
     }
 
-
     #}
     ($const eq "o" || $const eq "e") or return;   # FIXME: why not have a useful return value?
     $query = qq/
@@ -1720,21 +1721,21 @@ sub _koha_notify_reserve {
 
     my $admin_email_address = $branch_details->{'branchemail'} || C4::Context->preference('KohaAdminEmailAddress');
 
-    my $letter = getletter( 'reserves', $letter_code );
-    die "Could not find a letter called '$letter_code' in the 'reserves' module" unless( $letter );
+    my $letter =  C4::Letters::GetPreparedLetter (
+        module => 'reserves',
+        letter_code => $letter_code,
+        branchcode => $reserve->{branchcode},
+        tables => {
+            'branches'  => $branch_details,
+            'borrowers' => $borrower,
+            'biblio'    => $biblionumber,
+            'reserves'  => $reserve,
+            'items', $reserve->{'itemnumber'},
+        },
+        substitute => { today => C4::Dates->new()->output() },
+    ) or die "Could not find a letter called '$letter_code' in the 'reserves' module";
 
-    C4::Letters::parseletter( $letter, 'branches', $reserve->{'branchcode'} );
-    C4::Letters::parseletter( $letter, 'borrowers', $borrowernumber );
-    C4::Letters::parseletter( $letter, 'biblio', $biblionumber );
-    C4::Letters::parseletter( $letter, 'reserves', $borrowernumber, $biblionumber );
 
-    if ( $reserve->{'itemnumber'} ) {
-        C4::Letters::parseletter( $letter, 'items', $reserve->{'itemnumber'} );
-    }
-    my $today = C4::Dates->new()->output();
-    $letter->{'title'} =~ s/<<today>>/$today/g;
-    $letter->{'content'} =~ s/<<today>>/$today/g;
-    $letter->{'content'} =~ s/<<[a-z0-9_]+\.[a-z0-9]+>>//g; #remove any stragglers
 
     if ( $print_mode ) {
         C4::Letters::EnqueueLetter( {
@@ -1861,6 +1862,36 @@ sub MergeHolds {
 }
 
 
+=head2 ReserveSlip
+
+  ReserveSlip($branchcode, $borrowernumber, $biblionumber)
+
+  Returns letter hash ( see C4::Letters::GetPreparedLetter ) or undef
+
+=cut
+
+sub ReserveSlip {
+    my ($branch, $borrowernumber, $biblionumber) = @_;
+
+#   return unless ( C4::Context->boolean_preference('printreserveslips') );
+
+    my $reserve = GetReserveInfo($borrowernumber,$biblionumber )
+      or return;
+
+    return  C4::Letters::GetPreparedLetter (
+        module => 'circulation',
+        letter_code => 'RESERVESLIP',
+        branchcode => $branch,
+        tables => {
+            'reserves'    => $reserve,
+            'branches'    => $reserve->{branchcode},
+            'borrowers'   => $reserve,
+            'biblio'      => $reserve,
+            'items'       => $reserve,
+        },
+    );
+}
+
 =head1 AUTHOR
 
 Koha Development Team <http://koha-community.org/>
diff --git a/C4/Suggestions.pm b/C4/Suggestions.pm
index 1a93f2e..8a8459b 100644
--- a/C4/Suggestions.pm
+++ b/C4/Suggestions.pm
@@ -371,20 +371,24 @@ sub ModSuggestion {
     if ($suggestion->{STATUS}) {
         # fetch the entire updated suggestion so that we can populate the letter
         my $full_suggestion = GetSuggestion($suggestion->{suggestionid});
-        my $letter = C4::Letters::getletter('suggestions', $full_suggestion->{STATUS});
-        if ($letter) {
-            C4::Letters::parseletter($letter, 'branches',    $full_suggestion->{branchcode});
-            C4::Letters::parseletter($letter, 'borrowers',   $full_suggestion->{suggestedby});
-            C4::Letters::parseletter($letter, 'suggestions', $full_suggestion->{suggestionid});
-            C4::Letters::parseletter($letter, 'biblio',      $full_suggestion->{biblionumber});
-            my $enqueued = C4::Letters::EnqueueLetter({
+        if ( my $letter =  C4::Letters::GetPreparedLetter (
+            module => 'suggestions',
+            letter_code => $full_suggestion->{STATUS},
+            branchcode => $full_suggestion->{branchcode},
+            tables => {
+                'branches'    => $full_suggestion->{branchcode},
+                'borrowers'   => $full_suggestion->{suggestedby},
+                'suggestions' => $full_suggestion,
+                'biblio'      => $full_suggestion->{biblionumber},
+            },
+        ) ) {
+            C4::Letters::EnqueueLetter({
                 letter                  => $letter,
                 borrowernumber          => $full_suggestion->{suggestedby},
                 suggestionid            => $full_suggestion->{suggestionid},
                 LibraryName             => C4::Context->preference("LibraryName"),
                 message_transport_type  => 'email',
-            });
-            if (!$enqueued){warn "can't enqueue letter $letter";}
+            }) or warn "can't enqueue letter $letter";
         }
     }
     return $status_update_table;
diff --git a/acqui/booksellers.pl b/acqui/booksellers.pl
index 2b16fe4..745d040 100755
--- a/acqui/booksellers.pl
+++ b/acqui/booksellers.pl
@@ -111,12 +111,11 @@ for my $vendor (@suppliers) {
     
     for my $basket ( @{$baskets} ) {
         my $authorisedby = $basket->{authorisedby};
-        my $basketbranch = GetMember( borrowernumber => $authorisedby )->{branchcode};
         
         if ($userenv->{'flags'} & 1 || #user is superlibrarian
                (haspermission( $uid, { acquisition => q{*} } ) && #user has acq permissions and
                    ($viewbaskets eq 'all' || #user is allowed to see all baskets
-                   ($viewbaskets eq 'branch' && $authorisedby && $userbranch eq $basketbranch) || #basket belongs to user's branch
+                   ($viewbaskets eq 'branch' && $authorisedby && $userbranch eq GetMember( borrowernumber => $authorisedby )->{branchcode}) || #basket belongs to user's branch
                    ($basket->{authorisedby} &&  $viewbaskets == 'user' && $authorisedby == $loggedinuser) #user created this basket
                    ) 
                 ) 
diff --git a/circ/circulation.pl b/circ/circulation.pl
index efb87da..8d0abfb 100755
--- a/circ/circulation.pl
+++ b/circ/circulation.pl
@@ -24,7 +24,6 @@ use strict;
 #use warnings; FIXME - Bug 2505
 use CGI;
 use C4::Output;
-use C4::Print;
 use C4::Auth qw/:DEFAULT get_session/;
 use C4::Dates qw/format_date/;
 use C4::Branch; # GetBranches
@@ -175,7 +174,7 @@ if ( $barcode eq '' && $query->param('charges') eq 'yes' ) {
 }
 
 if ( $print eq 'yes' && $borrowernumber ne '' ) {
-    printslip( $borrowernumber );
+    PrintIssueSlip($branch, $borrowernumber);
     $query->param( 'borrowernumber', '' );
     $borrowernumber = '';
 }
diff --git a/circ/hold-transfer-slip.pl b/circ/hold-transfer-slip.pl
index f581464..492dac7 100755
--- a/circ/hold-transfer-slip.pl
+++ b/circ/hold-transfer-slip.pl
@@ -25,8 +25,6 @@ use C4::Output;
 use CGI;
 use C4::Auth;
 use C4::Reserves;
-use C4::Branch;
-use C4::Dates qw/format_date format_date_in_iso/;
 
 use vars qw($debug);
 
@@ -41,7 +39,7 @@ my $transfer = $input->param('transfer');
 
 my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
     {   
-        template_name   => "circ/hold-transfer-slip.tmpl",
+        template_name   => "circ/printslip.tmpl",
         query           => $input,
         type            => "intranet",
         authnotrequired => 0,
@@ -50,14 +48,21 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
     }
 );
 
-my $reserveinfo = GetReserveInfo($borrowernumber,$biblionumber );
-my $pulldate = C4::Dates->new();
-$reserveinfo->{'pulldate'} = $pulldate->output();
-$reserveinfo->{'branchname'} = GetBranchName($reserveinfo->{'branchcode'});
-$reserveinfo->{'transferrequired'} = $transfer;
-
-$template->param( reservedata => [ $reserveinfo ] ,
-				);
+my $userenv = C4::Context->userenv;
+my ($slip, $is_html);
+if ( my $letter = ReserveSlip ($userenv->{branch}, $borrowernumber, $biblionumber) ) {
+    $slip = $letter->{content};
+    $is_html = $letter->{is_html};
+}
+else {
+    $slip = "Reserve not found";
+}
+$template->param(
+    slip => $slip,
+    plain => !$is_html,
+    title => "Koha -- Circulation: Transfers",
+    stylesheet => C4::Context->preference("SlipCSS"),
+);
 
 output_html_with_http_headers $input, $cookie, $template->output;
 
diff --git a/installer/data/mysql/de-DE/mandatory/sample_notices.sql b/installer/data/mysql/de-DE/mandatory/sample_notices.sql
index 166c36d..efdad91 100644
--- a/installer/data/mysql/de-DE/mandatory/sample_notices.sql
+++ b/installer/data/mysql/de-DE/mandatory/sample_notices.sql
@@ -11,7 +11,7 @@ VALUES ('circulation','ODUE','Mahnung','Mahnung','Liebe/r <<borrowers.firstname>
 ('reserves', 'HOLD_PRINT', 'Vormerkbenachrichtigung (Print)', 'Vormerkbenachrichtigung (Print)', '<<branches.branchname>>\r\n<<branches.branchaddress1>>\r\n<<branches.branchaddress2>>\r\n<<branches.branchaddress2>>\r\n<<branches.branchzip>> <<branches.branchcity>>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<<borrowers.firstname>> <<borrowers.surname>>\r\n<<borrowers.address>>\r\n<<borrowers.address2>>\r\n<<borrowers.zipcode>> <<borrowers.city>>\r\n<<borrowers.country>>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nLiebe(r) <<borrowers.firstname>> <<borrowers.surname>>,\r\n\r\nFür Sie liegt seit dem <<reserves.waitingdate>> eine Vormerkung zur Abholung bereit:\r\n\r\nTitel: <<biblio.title>>\r\nVerfasser: <<biblio.author>>\r\nSignatur: <<items.itemcallnumber>>\r\n'),
 ('circulation','CHECKIN','Rückgabequittung (Zusammenfassung)','Rückgabequittung','Die folgenden Medien wurden zurückgegeben:\r\n----\r\n<<biblio.title>>\r\n----\r\nVielen Dank.'),
 ('circulation','CHECKOUT','Ausleihquittung (Zusammenfassung)','Ausleihquittung','Die folgenden Medien wurden entliehen:\r\n----\r\n<<biblio.title>>\r\n----\r\nVielen Dank für Ihren Besuch in <<branches.branchname>>.'),
-('reserves', 'HOLDPLACED', 'Neue Vormerkung', 'Neue Vormerkung','Folgender Titel wurde vorgemerkt: <<title>> (<<biblionumber>>) durch den Benutzer <<firstname>> <<surname>> (<<cardnumber>>).'),
+('reserves', 'HOLDPLACED', 'Neue Vormerkung', 'Neue Vormerkung','Folgender Titel wurde vorgemerkt: <<biblio.title>> (<<biblio.biblionumber>>) durch den Benutzer <<borrowers.firstname>> <<borrowers.surname>> (<<borrowers.cardnumber>>).'),
 ('suggestions','ACCEPTED','Anschaffungsvorschlag wurde angenommen', 'Ihr Anschaffungsvorschlag wurde angenommen','Liebe(r) <<borrowers.firstname>> <<borrowers.surname>>,\n\nSie haben der Bibliothek folgendes Medium zur Anschaffung vorgeschlagen: <<suggestions.title>> by <<suggestions.author>>.\n\nDie Bibliothek hat diesen Titel heute recherchiert und wird Ihn sobald wie möglich im Buchhandel bestellen. Sie erhalten Nachricht, sobald die Bestellung abgeschlossen ist und sobald der Titel in der Bibliotek verfügbar ist.\n\nWenn Sie Fragen haben, richten Sie Ihre Mail bitte an: <<branches.branchemail>>.\n\nVielen Dank,\n\n<<branches.branchname>>'),
 ('suggestions','AVAILABLE','Vorgeschlagenes Medium verfügbar', 'Das vorgeschlagene Medium ist jetzt verfügbar','Liebe(r) <<borrowers.firstname>> <<borrowers.surname>>,\n\nSie haben der Bibliothek folgendes Medium zur Anschaffung vorgeschlagen: <<suggestions.title>> von <<suggestions.author>>.\n\nWir freuen uns Ihnen mitteilen zu können, dass dieser Titel jetzt im Bestand der Bibliothek verfügbar ist.\n\nWenn Sie Fragen haben, richten Sie Ihre Mail bitte an: <<branches.branchemail>>.\n\nVielen Dank,\n\n<<branches.branchname>>'),
 ('suggestions','ORDERED','Vorgeschlagenes Medium bestellt', 'Das vorgeschlagene Medium wurde im Buchhandel bestellt','Liebe(r) <<borrowers.firstname>> <<borrowers.surname>>,\n\nSie haben der Bibliothek folgendes Medium zur Anschaffung vorgeschlaten: <<suggestions.title>> von <<suggestions.author>>.\n\nWir freuen uns Ihnen mitteilen zu können, dass dieser Titel jetzt im Buchhandel bestellt wurde. Nach Eintreffen wird er in unseren Bestand eingearbeitet.\n\nSie erhalten Nachricht, sobald das Medium verfügbar ist.\n\nBei Nachfragen erreichen Sie uns unter der Emailadresse <<branches.branchemail>>.\n\nVielen Dank,\n\n<<branches.branchname>>'),
diff --git a/installer/data/mysql/en/mandatory/sample_notices.sql b/installer/data/mysql/en/mandatory/sample_notices.sql
index 689fa0f..8c9f4bd 100644
--- a/installer/data/mysql/en/mandatory/sample_notices.sql
+++ b/installer/data/mysql/en/mandatory/sample_notices.sql
@@ -11,8 +11,90 @@ VALUES ('circulation','ODUE','Overdue Notice','Item Overdue','Dear <<borrowers.f
 ('reserves', 'HOLD_PRINT', 'Hold Available for Pickup (print notice)', 'Hold Available for Pickup (print notice)', '<<branches.branchname>>\r\n<<branches.branchaddress1>>\r\n<<branches.branchaddress2>>\r\n\r\n\r\nChange Service Requested\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<<borrowers.firstname>> <<borrowers.surname>>\r\n<<borrowers.address>>\r\n<<borrowers.city>> <<borrowers.zipcode>>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<<borrowers.firstname>> <<borrowers.surname>> <<borrowers.cardnumber>>\r\n\r\nYou have a hold available for pickup as of <<reserves.waitingdate>>:\r\n\r\nTitle: <<biblio.title>>\r\nAuthor: <<biblio.author>>\r\nCopy: <<items.copynumber>>\r\n'),
 ('circulation','CHECKIN','Item Check-in (Digest)','Check-ins','The following items have been checked in:\r\n----\r\n<<biblio.title>>\r\n----\r\nThank you.'),
 ('circulation','CHECKOUT','Item Check-out (Digest)','Checkouts','The following items have been checked out:\r\n----\r\n<<biblio.title>>\r\n----\r\nThank you for visiting <<branches.branchname>>.'),
-('reserves', 'HOLDPLACED', 'Hold Placed on Item', 'Hold Placed on Item','A hold has been placed on the following item : <<title>> (<<biblionumber>>) by the user <<firstname>> <<surname>> (<<cardnumber>>).'),
+('reserves', 'HOLDPLACED', 'Hold Placed on Item', 'Hold Placed on Item','A hold has been placed on the following item : <<biblio.title>> (<<biblio.biblionumber>>) by the user <<borrowers.firstname>> <<borrowers.surname>> (<<borrowers.cardnumber>>).'),
 ('suggestions','ACCEPTED','Suggestion accepted', 'Purchase suggestion accepted','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nThe library has reviewed your suggestion today. The item will be ordered as soon as possible. You will be notified by mail when the order is completed, and again when the item arrives at the library.\n\nIf you have any questions, please email us at <<branches.branchemail>>.\n\nThank you,\n\n<<branches.branchname>>'),
 ('suggestions','AVAILABLE','Suggestion available', 'Suggested purchase available','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nWe are pleased to inform you that the item you requested is now part of the collection.\n\nIf you have any questions, please email us at <<branches.branchemail>>.\n\nThank you,\n\n<<branches.branchname>>'),
 ('suggestions','ORDERED','Suggestion ordered', 'Suggested item ordered','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nWe are pleased to inform you that the item you requested has now been ordered. It should arrive soon, at which time it will be processed for addition into the collection.\n\nYou will be notified again when the book is available.\n\nIf you have any questions, please email us at <<branches.branchemail>>\n\nThank you,\n\n<<branches.branchname>>'),
 ('suggestions','REJECTED','Suggestion rejected', 'Purchase suggestion declined','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nThe library has reviewed your request today, and has decided not to accept the suggestion at this time.\n\nThe reason given is: <<suggestions.reason>>\n\nIf you have any questions, please email us at <<branches.branchemail>>.\n\nThank you,\n\n<<branches.branchname>>');
+INSERT INTO `letter` (module, code, name, title, content, is_html)
+VALUES ('circulation','ISSUESLIP','Issue Slip','Issue Slip', '<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>><br />
+</p>
+</checkedout>
+
+<h4>Overdues</h4>
+<overdue>
+<p>
+<<biblio.title>> <br />
+Barcode: <<items.barcode>><br />
+Date due: <<issues.date_due>><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.new>></p>
+<p class="newsfooter" style="font-size: 8pt; font-style:italic; margin-bottom: 1px; margin-top: 1px">Posted on <<opac_news.timestamp>></p>
+<hr />
+</div>
+</news>', 1),
+('circulation','ISSUEQSLIP','Issue Quick Slip','Issue Quick Slip', '<h3><<branches.branchname>></h3>
+Checked out to <<borrowers.title>> <<borrowers.firstname>> <<borrowers.initials>> <<borrowers.surname>> <br />
+(<<borrowers.cardnumber>>) <br />
+
+<<today>><br />
+
+<h4>Checked Out Today</h4>
+<checkedout>
+<p>
+<<biblio.title>> <br />
+Barcode: <<items.barcode>><br />
+Date due: <<issues.date_due>><br />
+</p>
+</checkedout>', 1),
+('circulation','RESERVESLIP','Reserve Slip','Reserve Slip', '<h5>Date: <<today>></h5>
+
+<h3> Transfer to/Hold in <<branches.branchname>></h3>
+
+<reserves>
+<div>
+<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.reservenotes>></pre>
+ </p>
+</div>
+</reserves>', 1);
+
diff --git a/installer/data/mysql/es-ES/mandatory/sample_notices.sql b/installer/data/mysql/es-ES/mandatory/sample_notices.sql
index 689fa0f..bf3324d 100644
--- a/installer/data/mysql/es-ES/mandatory/sample_notices.sql
+++ b/installer/data/mysql/es-ES/mandatory/sample_notices.sql
@@ -11,7 +11,7 @@ VALUES ('circulation','ODUE','Overdue Notice','Item Overdue','Dear <<borrowers.f
 ('reserves', 'HOLD_PRINT', 'Hold Available for Pickup (print notice)', 'Hold Available for Pickup (print notice)', '<<branches.branchname>>\r\n<<branches.branchaddress1>>\r\n<<branches.branchaddress2>>\r\n\r\n\r\nChange Service Requested\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<<borrowers.firstname>> <<borrowers.surname>>\r\n<<borrowers.address>>\r\n<<borrowers.city>> <<borrowers.zipcode>>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<<borrowers.firstname>> <<borrowers.surname>> <<borrowers.cardnumber>>\r\n\r\nYou have a hold available for pickup as of <<reserves.waitingdate>>:\r\n\r\nTitle: <<biblio.title>>\r\nAuthor: <<biblio.author>>\r\nCopy: <<items.copynumber>>\r\n'),
 ('circulation','CHECKIN','Item Check-in (Digest)','Check-ins','The following items have been checked in:\r\n----\r\n<<biblio.title>>\r\n----\r\nThank you.'),
 ('circulation','CHECKOUT','Item Check-out (Digest)','Checkouts','The following items have been checked out:\r\n----\r\n<<biblio.title>>\r\n----\r\nThank you for visiting <<branches.branchname>>.'),
-('reserves', 'HOLDPLACED', 'Hold Placed on Item', 'Hold Placed on Item','A hold has been placed on the following item : <<title>> (<<biblionumber>>) by the user <<firstname>> <<surname>> (<<cardnumber>>).'),
+('reserves', 'HOLDPLACED', 'Hold Placed on Item', 'Hold Placed on Item','A hold has been placed on the following item : <<biblio.title>> (<<biblio.biblionumber>>) by the user <<borrowers.firstname>> <<borrowers.surname>> (<<borrowers.cardnumber>>).'),
 ('suggestions','ACCEPTED','Suggestion accepted', 'Purchase suggestion accepted','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nThe library has reviewed your suggestion today. The item will be ordered as soon as possible. You will be notified by mail when the order is completed, and again when the item arrives at the library.\n\nIf you have any questions, please email us at <<branches.branchemail>>.\n\nThank you,\n\n<<branches.branchname>>'),
 ('suggestions','AVAILABLE','Suggestion available', 'Suggested purchase available','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nWe are pleased to inform you that the item you requested is now part of the collection.\n\nIf you have any questions, please email us at <<branches.branchemail>>.\n\nThank you,\n\n<<branches.branchname>>'),
 ('suggestions','ORDERED','Suggestion ordered', 'Suggested item ordered','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nWe are pleased to inform you that the item you requested has now been ordered. It should arrive soon, at which time it will be processed for addition into the collection.\n\nYou will be notified again when the book is available.\n\nIf you have any questions, please email us at <<branches.branchemail>>\n\nThank you,\n\n<<branches.branchname>>'),
diff --git a/installer/data/mysql/fr-FR/1-Obligatoire/sample_notices.sql b/installer/data/mysql/fr-FR/1-Obligatoire/sample_notices.sql
index 977e59d..acffe73 100644
--- a/installer/data/mysql/fr-FR/1-Obligatoire/sample_notices.sql
+++ b/installer/data/mysql/fr-FR/1-Obligatoire/sample_notices.sql
@@ -13,7 +13,7 @@ VALUES
 ('reserves', 'HOLD_PRINT', 'Hold Available for Pickup (print notice)', 'Hold Available for Pickup at <<branches.branchname>>', '<<branches.branchname>>\n<<branches.branchaddress1>>\n<<branches.branchaddress2>>\n\n\nChange Service Requested\n\n\n\n\n\n\n\n<<borrowers.firstname>> <<borrowers.surname>>\n<<borrowers.address>>\n<<borrowers.city>> <<borrowers.zipcode>>\n\n\n\n\n\n\n\n\n\n\n<<borrowers.firstname>> <<borrowers.surname>> <<borrowers.cardnumber>>\n\nYou have a hold available for pickup as of <<reserves.waitingdate>>:\r\n\r\nTitle: <<biblio.title>>\r\nAuthor: <<biblio.author>>\r\nCopy: <<items.copynumber>>\r\n'),
 ('circulation','CHECKIN','Item Check-in (Digest)','Check-ins','The following items have been checked in:\r\n----\r\n<<biblio.title>>\r\n----\r\nThank you.'),
 ('circulation','CHECKOUT','Item Check-out (Digest)','Checkouts','The following items have been checked out:\r\n----\r\n<<biblio.title>>\r\n----\r\nThank you for visiting <<branches.branchname>>.'),
-('reserves', 'HOLDPLACED', 'Hold Placed on Item', 'Hold Placed on Item','A hold has been placed on the following item : <<title>> (<<biblionumber>>) by the user <<firstname>> <<surname>> (<<cardnumber>>).'),
+('reserves', 'HOLDPLACED', 'Hold Placed on Item', 'Hold Placed on Item','A hold has been placed on the following item : <<biblio.title>> (<<biblio.biblionumber>>) by the user <<borrowers.firstname>> <<borrowers.surname>> (<<borrowers.cardnumber>>).'),
 ('suggestions','ACCEPTED','Suggestion accepted', 'Purchase suggestion accepted','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nThe library has reviewed your suggestion today. The item will be ordered as soon as possible. You will be notified by mail when the order is completed, and again when the item arrives at the library.\n\nIf you have any questions, please email us at <<branches.branchemail>>.\n\nThank you,\n\n<<branches.branchname>>'),
 ('suggestions','AVAILABLE','Suggestion available', 'Suggested purchase available','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nWe are pleased to inform you that the item you requested is now part of the collection.\n\nIf you have any questions, please email us at <<branches.branchemail>>.\n\nThank you,\n\n<<branches.branchname>>'),
 ('suggestions','ORDERED','Suggestion ordered', 'Suggested item ordered','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nWe are pleased to inform you that the item you requested has now been ordered. It should arrive soon, at which time it will be processed for addition into the collection.\n\nYou will be notified again when the book is available.\n\nIf you have any questions, please email us at <<branches.branchemail>>\n\nThank you,\n\n<<branches.branchname>>'),
diff --git a/installer/data/mysql/it-IT/necessari/notices.sql b/installer/data/mysql/it-IT/necessari/notices.sql
index 689fa0f..bf3324d 100644
--- a/installer/data/mysql/it-IT/necessari/notices.sql
+++ b/installer/data/mysql/it-IT/necessari/notices.sql
@@ -11,7 +11,7 @@ VALUES ('circulation','ODUE','Overdue Notice','Item Overdue','Dear <<borrowers.f
 ('reserves', 'HOLD_PRINT', 'Hold Available for Pickup (print notice)', 'Hold Available for Pickup (print notice)', '<<branches.branchname>>\r\n<<branches.branchaddress1>>\r\n<<branches.branchaddress2>>\r\n\r\n\r\nChange Service Requested\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<<borrowers.firstname>> <<borrowers.surname>>\r\n<<borrowers.address>>\r\n<<borrowers.city>> <<borrowers.zipcode>>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<<borrowers.firstname>> <<borrowers.surname>> <<borrowers.cardnumber>>\r\n\r\nYou have a hold available for pickup as of <<reserves.waitingdate>>:\r\n\r\nTitle: <<biblio.title>>\r\nAuthor: <<biblio.author>>\r\nCopy: <<items.copynumber>>\r\n'),
 ('circulation','CHECKIN','Item Check-in (Digest)','Check-ins','The following items have been checked in:\r\n----\r\n<<biblio.title>>\r\n----\r\nThank you.'),
 ('circulation','CHECKOUT','Item Check-out (Digest)','Checkouts','The following items have been checked out:\r\n----\r\n<<biblio.title>>\r\n----\r\nThank you for visiting <<branches.branchname>>.'),
-('reserves', 'HOLDPLACED', 'Hold Placed on Item', 'Hold Placed on Item','A hold has been placed on the following item : <<title>> (<<biblionumber>>) by the user <<firstname>> <<surname>> (<<cardnumber>>).'),
+('reserves', 'HOLDPLACED', 'Hold Placed on Item', 'Hold Placed on Item','A hold has been placed on the following item : <<biblio.title>> (<<biblio.biblionumber>>) by the user <<borrowers.firstname>> <<borrowers.surname>> (<<borrowers.cardnumber>>).'),
 ('suggestions','ACCEPTED','Suggestion accepted', 'Purchase suggestion accepted','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nThe library has reviewed your suggestion today. The item will be ordered as soon as possible. You will be notified by mail when the order is completed, and again when the item arrives at the library.\n\nIf you have any questions, please email us at <<branches.branchemail>>.\n\nThank you,\n\n<<branches.branchname>>'),
 ('suggestions','AVAILABLE','Suggestion available', 'Suggested purchase available','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nWe are pleased to inform you that the item you requested is now part of the collection.\n\nIf you have any questions, please email us at <<branches.branchemail>>.\n\nThank you,\n\n<<branches.branchname>>'),
 ('suggestions','ORDERED','Suggestion ordered', 'Suggested item ordered','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nWe are pleased to inform you that the item you requested has now been ordered. It should arrive soon, at which time it will be processed for addition into the collection.\n\nYou will be notified again when the book is available.\n\nIf you have any questions, please email us at <<branches.branchemail>>\n\nThank you,\n\n<<branches.branchname>>'),
diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql
index e4388a4..59ee5f3 100644
--- a/installer/data/mysql/kohastructure.sql
+++ b/installer/data/mysql/kohastructure.sql
@@ -1166,10 +1166,12 @@ DROP TABLE IF EXISTS `letter`;
 CREATE TABLE `letter` ( -- table for all notice templates in Koha
   `module` varchar(20) NOT NULL default '', -- Koha module that triggers this notice
   `code` varchar(20) NOT NULL default '', -- unique identifier for this notice
+  `branchcode` varchar(10) default NULL, -- foreign key, linking to the branches table for the location the item was checked out
   `name` varchar(100) NOT NULL default '', -- plain text name for this notice
+  `is_html` tinyint(1) default 0,
   `title` varchar(200) NOT NULL default '', -- subject line of the notice
   `content` text, -- body text for the notice
-  PRIMARY KEY  (`module`,`code`)
+  PRIMARY KEY  (`module`,`code`, `branchcode`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 --
diff --git a/installer/data/mysql/nb-NO/1-Obligatorisk/sample_notices.sql b/installer/data/mysql/nb-NO/1-Obligatorisk/sample_notices.sql
index cdb5529..08b452e 100644
--- a/installer/data/mysql/nb-NO/1-Obligatorisk/sample_notices.sql
+++ b/installer/data/mysql/nb-NO/1-Obligatorisk/sample_notices.sql
@@ -32,7 +32,7 @@ VALUES ('circulation','ODUE','Purring','Purring på dokument','<<borrowers.first
 ('reserves', 'HOLD_PRINT', 'Hentemelding (på papir)', 'Hentemelding', '<<branches.branchname>>\r\n<<branches.branchaddress1>>\r\n<<branches.branchaddress2>>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<<borrowers.firstname>> <<borrowers.surname>>\r\n<<borrowers.address>>\r\n<<borrowers.city>> <<borrowers.zipcode>>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<<borrowers.firstname>> <<borrowers.surname>> <<borrowers.cardnumber>>\r\n\r\nDu har et reservert dokument som kan hentes fra  <<reserves.waitingdate>>:\r\n\r\nTittel: <<biblio.title>>\r\nForfatter: <<biblio.author>>\r\nEksemplar: <<items.copynumber>>\r\n'),
 ('circulation','CHECKIN','Innlevering','Melding om innlevering','Følgende dokument har blitt innlevert:\r\n----\r\n<<biblio.title>>\r\n----\r\nVennlig hilsen\r\nBiblioteket'),
 ('circulation','CHECKOUT','Utlån','Melding om utlån','Følgende dokument har blitt lånt ut:\r\n----\r\n<<biblio.title>>\r\n----\r\nVennlig hilsen\r\nBiblioteket'),
-('reserves', 'HOLDPLACED', 'Melding om reservasjon', 'Melding om reservasjon','Følgende dokument har blitt reservert : <<title>> (<<biblionumber>>) av <<firstname>> <<surname>> (<<cardnumber>>).'),
+('reserves', 'HOLDPLACED', 'Melding om reservasjon', 'Melding om reservasjon','Følgende dokument har blitt reservert : <<biblio.title>> (<<biblio.biblionumber>>) av <<borrowers.firstname>> <<borrowers.surname>> (<<borrowers.cardnumber>>).'),
 ('suggestions','ACCEPTED','Forslag godtatt', 'Innkjøpsforslag godtatt','<<borrowers.firstname>> <<borrowers.surname>>,\n\nDu har foreslått at biblioteket kjøper inn <<suggestions.title>> av <<suggestions.author>>.\n\nBiblioteket har vurdert forslaget i dag. Dokumentet vil bli bestilt så fort det lar seg gjøre. Du vil få en ny melding når bestillingen er gjort, og når dokumentet ankommer biblioteket.\n\nEr det noe du lurer på, vennligst kontakt oss på <<branches.branchemail>>.\n\nVennlig hilsen,\n\n<<branches.branchname>>'),
 ('suggestions','AVAILABLE','Foreslått dokument tilgjengelig', 'Foreslått dokument tilgjengelig','<<borrowers.firstname>> <<borrowers.surname>>,\n\nDu har foreslått at biblioteket kjøper inn <<suggestions.title>> av <<suggestions.author>>.\n\nVi har gleden av å informere deg om at dokumentet nå er innlemmet i samlingen.\n\nEr det noe du lurer på, vennligst kontakt oss på <<branches.branchemail>>.\n\nVennlig hilsen,\n\n<<branches.branchname>>'),
 ('suggestions','ORDERED','Innkjøpsforslag i bestilling', 'Innkjøpsforslag i bestilling','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nDu har foreslått at biblioteket kjøper inn <<suggestions.title>> av <<suggestions.author>>.\n\nVi har gleden av å informere deg om at dokumentet du foreslo nå er i bestilling.\n\nDu vil få en ny melding når dokumentet er tilgjengelig.\n\nEr det noe du lurer på, vennligst kontakt oss på <<branches.branchemail>>.\n\nVennlig hilsen,\n\n<<branches.branchname>>'),
diff --git a/installer/data/mysql/pl-PL/mandatory/sample_notices.sql b/installer/data/mysql/pl-PL/mandatory/sample_notices.sql
index 6be2eb8..f0844f3 100644
--- a/installer/data/mysql/pl-PL/mandatory/sample_notices.sql
+++ b/installer/data/mysql/pl-PL/mandatory/sample_notices.sql
@@ -13,7 +13,7 @@ VALUES
 ('reserves', 'HOLD_PRINT', 'Hold Available for Pickup (print notice)', 'Hold Available for Pickup (print notice)', '<<branches.branchname>>\r\n<<branches.branchaddress1>>\r\n<<branches.branchaddress2>>\r\n\r\n\r\nChange Service Requested\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<<borrowers.firstname>> <<borrowers.surname>>\r\n<<borrowers.address>>\r\n<<borrowers.city>> <<borrowers.zipcode>>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<<borrowers.firstname>> <<borrowers.surname>> <<borrowers.cardnumber>>\r\n\r\nYou have a hold available for pickup as of <<reserves.waitingdate>>:\r\n\r\nTitle: <<biblio.title>>\r\nAuthor: <<biblio.author>>\r\nCopy: <<items.copynumber>>\r\n'),
 ('circulation','CHECKIN','Item Check-in (Digest)','Check-ins','The following items have been checked in:\r\n----\r\n<<biblio.title>>\r\n----\r\nThank you.'),
 ('circulation','CHECKOUT','Item Check-out (Digest)','Checkouts','The following items have been checked out:\r\n----\r\n<<biblio.title>>\r\n----\r\nThank you for visiting <<branches.branchname>>.'),
-('reserves', 'HOLDPLACED', 'Hold Placed on Item', 'Hold Placed on Item','A hold has been placed on the following item : <<title>> (<<biblionumber>>) by the user <<firstname>> <<surname>> (<<cardnumber>>).'),
+('reserves', 'HOLDPLACED', 'Hold Placed on Item', 'Hold Placed on Item','A hold has been placed on the following item : <<biblio.title>> (<<biblio.biblionumber>>) by the user <<borrowers.firstname>> <<borrowers.surname>> (<<borrowers.cardnumber>>).'),
 ('suggestions','ACCEPTED','Suggestion accepted', 'Purchase suggestion accepted','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nThe library has reviewed your suggestion today. The item will be ordered as soon as possible. You will be notified by mail when the order is completed, and again when the item arrives at the library.\n\nIf you have any questions, please email us at <<branches.branchemail>>.\n\nThank you,\n\n<<branches.branchname>>'),
 ('suggestions','AVAILABLE','Suggestion available', 'Suggested purchase available','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nWe are pleased to inform you that the item you requested is now part of the collection.\n\nIf you have any questions, please email us at <<branches.branchemail>>.\n\nThank you,\n\n<<branches.branchname>>'),
 ('suggestions','ORDERED','Suggestion ordered', 'Suggested item ordered','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nWe are pleased to inform you that the item you requested has now been ordered. It should arrive soon, at which time it will be processed for addition into the collection.\n\nYou will be notified again when the book is available.\n\nIf you have any questions, please email us at <<branches.branchemail>>\n\nThank you,\n\n<<branches.branchname>>'),
diff --git a/installer/data/mysql/ru-RU/mandatory/sample_notices.sql b/installer/data/mysql/ru-RU/mandatory/sample_notices.sql
index 689fa0f..bf3324d 100644
--- a/installer/data/mysql/ru-RU/mandatory/sample_notices.sql
+++ b/installer/data/mysql/ru-RU/mandatory/sample_notices.sql
@@ -11,7 +11,7 @@ VALUES ('circulation','ODUE','Overdue Notice','Item Overdue','Dear <<borrowers.f
 ('reserves', 'HOLD_PRINT', 'Hold Available for Pickup (print notice)', 'Hold Available for Pickup (print notice)', '<<branches.branchname>>\r\n<<branches.branchaddress1>>\r\n<<branches.branchaddress2>>\r\n\r\n\r\nChange Service Requested\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<<borrowers.firstname>> <<borrowers.surname>>\r\n<<borrowers.address>>\r\n<<borrowers.city>> <<borrowers.zipcode>>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<<borrowers.firstname>> <<borrowers.surname>> <<borrowers.cardnumber>>\r\n\r\nYou have a hold available for pickup as of <<reserves.waitingdate>>:\r\n\r\nTitle: <<biblio.title>>\r\nAuthor: <<biblio.author>>\r\nCopy: <<items.copynumber>>\r\n'),
 ('circulation','CHECKIN','Item Check-in (Digest)','Check-ins','The following items have been checked in:\r\n----\r\n<<biblio.title>>\r\n----\r\nThank you.'),
 ('circulation','CHECKOUT','Item Check-out (Digest)','Checkouts','The following items have been checked out:\r\n----\r\n<<biblio.title>>\r\n----\r\nThank you for visiting <<branches.branchname>>.'),
-('reserves', 'HOLDPLACED', 'Hold Placed on Item', 'Hold Placed on Item','A hold has been placed on the following item : <<title>> (<<biblionumber>>) by the user <<firstname>> <<surname>> (<<cardnumber>>).'),
+('reserves', 'HOLDPLACED', 'Hold Placed on Item', 'Hold Placed on Item','A hold has been placed on the following item : <<biblio.title>> (<<biblio.biblionumber>>) by the user <<borrowers.firstname>> <<borrowers.surname>> (<<borrowers.cardnumber>>).'),
 ('suggestions','ACCEPTED','Suggestion accepted', 'Purchase suggestion accepted','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nThe library has reviewed your suggestion today. The item will be ordered as soon as possible. You will be notified by mail when the order is completed, and again when the item arrives at the library.\n\nIf you have any questions, please email us at <<branches.branchemail>>.\n\nThank you,\n\n<<branches.branchname>>'),
 ('suggestions','AVAILABLE','Suggestion available', 'Suggested purchase available','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nWe are pleased to inform you that the item you requested is now part of the collection.\n\nIf you have any questions, please email us at <<branches.branchemail>>.\n\nThank you,\n\n<<branches.branchname>>'),
 ('suggestions','ORDERED','Suggestion ordered', 'Suggested item ordered','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nWe are pleased to inform you that the item you requested has now been ordered. It should arrive soon, at which time it will be processed for addition into the collection.\n\nYou will be notified again when the book is available.\n\nIf you have any questions, please email us at <<branches.branchemail>>\n\nThank you,\n\n<<branches.branchname>>'),
diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql
index ae2c1cb..f10a7b3 100755
--- a/installer/data/mysql/sysprefs.sql
+++ b/installer/data/mysql/sysprefs.sql
@@ -328,4 +328,7 @@ INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('
 INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('OpacKohaUrl','1',"Show 'Powered by Koha' text on OPAC footer.",NULL,NULL);
 INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('EasyAnalyticalRecords','0','If on, display in the catalogue screens tools to easily setup analytical record relationships','','YesNo');
 INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('OpacShowRecentComments',0,'If ON a link to recent comments will appear in the OPAC masthead',NULL,'YesNo');
+INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('NoticeCSS','','Notices CSS url.',NULL,'free');
+INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('SlipCSS','','Slips CSS url.',NULL,'free');
+
 
diff --git a/installer/data/mysql/uk-UA/mandatory/sample_notices.sql b/installer/data/mysql/uk-UA/mandatory/sample_notices.sql
index 358205b..b637343 100644
--- a/installer/data/mysql/uk-UA/mandatory/sample_notices.sql
+++ b/installer/data/mysql/uk-UA/mandatory/sample_notices.sql
@@ -10,7 +10,7 @@ VALUES ('circulation','ODUE','Overdue Notice','Item Overdue','Dear <<borrowers.f
 ('reserves', 'HOLD', 'Hold Available for Pickup', 'Hold Available for Pickup at <<branches.branchname>>', 'Dear <<borrowers.firstname>> <<borrowers.surname>>,\r\n\r\nYou have a hold available for pickup as of <<reserves.waitingdate>>:\r\n\r\nTitle: <<biblio.title>>\r\nAuthor: <<biblio.author>>\r\nCopy: <<items.copynumber>>\r\nLocation: <<branches.branchname>>\r\n<<branches.branchaddress1>>\r\n<<branches.branchaddress2>>\r\n<<branches.branchaddress3>>\r\n<<branches.branchcity>> <<branches.branchzip>>'),
 ('circulation','CHECKIN','Item Check-in (Digest)','Check-ins','The following items have been checked in:\r\n----\r\n<<biblio.title>>\r\n----\r\nThank you.'),
 ('circulation','CHECKOUT','Item Check-out (Digest)','Checkouts','The following items have been checked out:\r\n----\r\n<<biblio.title>>\r\n----\r\nThank you for visiting <<branches.branchname>>.'),
-('reserves', 'HOLDPLACED', 'Hold Placed on Item', 'Hold Placed on Item','A hold has been placed on the following item : <<title>> (<<biblionumber>>) by the user <<firstname>> <<surname>> (<<cardnumber>>).'),
+('reserves', 'HOLDPLACED', 'Hold Placed on Item', 'Hold Placed on Item','A hold has been placed on the following item : <<biblio.title>> (<<biblio.biblionumber>>) by the user <<borrowers.firstname>> <<borrowers.surname>> (<<borrowers.cardnumber>>).'),
 ('suggestions','ACCEPTED','Suggestion accepted', 'Purchase suggestion accepted','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nThe library has reviewed your suggestion today. The item will be ordered as soon as possible. You will be notified by mail when the order is completed, and again when the item arrives at the library.\n\nIf you have any questions, please email us at <<branches.branchemail>>.\n\nThank you,\n\n<<branches.branchname>>'),
 ('suggestions','AVAILABLE','Suggestion available', 'Suggested purchase available','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nWe are pleased to inform you that the item you requested is now part of the collection.\n\nIf you have any questions, please email us at <<branches.branchemail>>.\n\nThank you,\n\n<<branches.branchname>>'),
 ('suggestions','ORDERED','Suggestion ordered', 'Suggested item ordered','Dear <<borrowers.firstname>> <<borrowers.surname>>,\n\nYou have suggested that the library acquire <<suggestions.title>> by <<suggestions.author>>.\n\nWe are pleased to inform you that the item you requested has now been ordered. It should arrive soon, at which time it will be processed for addition into the collection.\n\nYou will be notified again when the book is available.\n\nIf you have any questions, please email us at <<branches.branchemail>>\n\nThank you,\n\n<<branches.branchname>>'),
diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl
index a8cd166..1151648 100755
--- a/installer/data/mysql/updatedatabase.pl
+++ b/installer/data/mysql/updatedatabase.pl
@@ -4496,7 +4496,6 @@ if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
         print "Upgrade to $DBversion done (Add 461 subfield 9 to default framework)\n";
         SetVersion ($DBversion);
     }
-		
 }
 
 $DBversion = "3.05.00.018";
@@ -4550,6 +4549,103 @@ if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
     SetVersion ($DBversion);
 }
 
+$DBversion = "3.06.00.XXX";
+if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
+    $dbh->do("ALTER TABLE `letter` DROP PRIMARY KEY");
+    $dbh->do("ALTER TABLE `letter` ADD `branchcode` varchar(10) default NULL AFTER `code`");
+    $dbh->do("ALTER TABLE `letter` ADD PRIMARY KEY  (`module`,`code`, `branchcode`)");
+    $dbh->do("ALTER TABLE `letter` ADD `is_html` tinyint(1) default 0 AFTER `name`");
+    print "Added branchcode and is_html to letter table\n";
+
+    $dbh->do("INSERT INTO `letter` (module, code, name, title, content, is_html)
+              VALUES ('circulation','ISSUESLIP','Issue Slip','Issue Slip', '<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>><br />
+</p>
+</checkedout>
+
+<h4>Overdues</h4>
+<overdue>
+<p>
+<<biblio.title>> <br />
+Barcode: <<items.barcode>><br />
+Date due: <<issues.date_due>><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.new>></p>
+<p class=\"newsfooter\" style=\"font-size: 8pt; font-style:italic; margin-bottom: 1px; margin-top: 1px\">Posted on <<opac_news.timestamp>></p>
+<hr />
+</div>
+</news>', 1)");
+    $dbh->do("INSERT INTO `letter` (module, code, name, title, content, is_html)
+              VALUES ('circulation','ISSUEQSLIP','Issue Quick Slip','Issue Quick Slip', '<h3><<branches.branchname>></h3>
+Checked out to <<borrowers.title>> <<borrowers.firstname>> <<borrowers.initials>> <<borrowers.surname>> <br />
+(<<borrowers.cardnumber>>) <br />
+
+<<today>><br />
+
+<h4>Checked Out Today</h4>
+<checkedout>
+<p>
+<<biblio.title>> <br />
+Barcode: <<items.barcode>><br />
+Date due: <<issues.date_due>><br />
+</p>
+</checkedout>', 1)");
+    $dbh->do("INSERT INTO `letter` (module, code, name, title, content, is_html)
+              VALUES ('circulation','RESERVESLIP','Reserve Slip','Reserve Slip', '<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.reservenotes>></pre>
+</p>', 1)");
+
+    $dbh->do("INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('NoticeCSS','','Notices CSS url.',NULL,'free')");
+    $dbh->do("INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('SlipCSS','','Slips CSS url.',NULL,'free')");
+
+    $dbh->do("UPDATE `letter` SET content = replace(content, '<<title>>', '<<biblio.title>>') WHERE code = 'HOLDPLACED')");
+
+    print "Upgrade to $DBversion done (Add branchcode and is_html to letter table; Add NoticeCSS and SlipCSS sysprefs)\n";
+    SetVersion($DBversion);
+}
+
 =head1 FUNCTIONS
 
 =head2 DropAllForeignKeys($table)
diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-toolbar.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-toolbar.inc
index 503f954..e7e150d 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-toolbar.inc
+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-toolbar.inc
@@ -42,8 +42,10 @@ function update_child() {
 	 });
 
 	// YUI Toolbar Functions
+    var slip_re = /slip/;
 	function printx_window(print_type) {
-		window.open("/cgi-bin/koha/members/moremember.pl?borrowernumber=[% borrowernumber %]&print=" + print_type, "printwindow");
+        var handler = print_type.match(slip_re) ? "printslip" : "moremember";
+		window.open("/cgi-bin/koha/members/" + handler + ".pl?borrowernumber=[% borrowernumber %]&print=" + print_type, "printwindow");
 		return false;
 	}
 	function searchToHold(){
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref
index f4946b5..b3b279c 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref
@@ -91,6 +91,11 @@ Circulation:
                   yes: Record
                   no: "Don't record"
             - local use when an unissued item is checked in.
+        -
+            - Include the stylesheet at
+            - pref: NoticeCSS
+              class: url
+            - on Notices. (This should be a complete URL, starting with <code>http://</code>.)
     Checkout Policy:
         -
             - pref: AllowNotForLoanOverride
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/staff_client.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/staff_client.pref
index df0a434..efa33a8 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/staff_client.pref
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/staff_client.pref
@@ -83,6 +83,11 @@ Staff Client:
                   Results: "Results page (for future use, Results XSLT not functional at this time)."
                   Both: "Both Results and Details pages (for future use, Results XSLT not functional at this time)."
             - 'Note: The corresponding XSLT option must be turned on.'
+        -
+            - Include the stylesheet at
+            - pref: SlipCSS
+              class: url
+            - on Issue and Reserve Slips. (This should be a complete URL, starting with <code>http://</code>.)
     Options:
         -
             - pref: viewMARC
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/batch/print-notices.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/batch/print-notices.tt
index 1904381..73f9e61 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/batch/print-notices.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/batch/print-notices.tt
@@ -8,11 +8,7 @@
         -->
     </style>
     [% IF ( stylesheet ) %]
-    <style type="text/css">
-        <!--
-        [% stylesheet %]
-        -->
-    </style>
+    <link rel="stylesheet" type="text/css" href="[% stylesheet %]">
     [% END %]
 </head>
 <body>
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/hold-transfer-slip.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/hold-transfer-slip.tt
deleted file mode 100644
index 18d45aa..0000000
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/hold-transfer-slip.tt
+++ /dev/null
@@ -1,54 +0,0 @@
-[% INCLUDE 'doc-head-open.inc' %]
-<title>Koha -- Circulation: Transfers</title>
-[% INCLUDE 'doc-head-close-receipt.inc' %]
-<script language="javascript">
-function printandclose()
-{
-window.print();
-window.close();
-}
-</script>
-</head>
-<body onload="printandclose();"><div id="main">
-
-[% FOREACH reservedat IN reservedata %]
-
-<h5>Date: [% reservedat.pulldate %]</h5>
-<h3> [% IF ( reservedat.transferrequired ) %]Transfer to [% reservedat.branchname %] [% ELSE %]Hold in [% reservedat.branchname %][% END %]</h3>
-
-<div id="reserve_display">
-
-<h3>[% reservedat.surname %], [% reservedat.firstname %]</h3>
-
-<ul>
-	<li>[% reservedat.cardnumber %]</li>
-    [% IF ( reservedat.phone ) %]
-        <li>[% reservedat.phone %]</li>
-    [% END %]
-    <li>
-        [% reservedat.address %]<br />
-	    [% IF ( reservedat.address2 ) %][% reservedat.address2 %]<br />[% END %]
-        [% reservedat.city %]  [% reservedat.zip %]
-    </li>
-    [% IF ( reservedat.email ) %]
-        <li>[% reservedat.email %]</li>
-    [% END %]
-</ul>
-<br />
-<h3>ITEM ON HOLD</h3>
- <h4>[% reservedat.title |html %]</h4>
- <h5>[% reservedat.author %] </h5>
- <ul>
-    [% IF ( reservedat.barcode ) %]<li>[% reservedat.barcode %]</li>[% END %]
-    [% IF ( reservedat.itemcallnumber ) %]<li>[% reservedat.itemcallnumber %]</li>[% END %]
-    [% IF ( reservedat.waitingdate ) %]<li>[% reservedat.waitingdate %]</li>[% END %]
- </ul>
- [% IF ( reservedat.reservenotes ) %]
-    <p>Notes: [% reservedat.reservenotes %]</p>
- [% END %]
-
-
-
-[% END %]
-</div>
-[% INCLUDE 'intranet-bottom.inc' %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/printslip.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/printslip.tt
new file mode 100644
index 0000000..a790069
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/printslip.tt
@@ -0,0 +1,28 @@
+[% INCLUDE 'doc-head-open.inc' %]
+<title>[% title %]</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<link rel="shortcut icon" href="[% IF ( IntranetFavicon ) %][% IntranetFavicon %][% ELSE %][% themelang %]/includes/favicon.ico[% END %]" type="image/x-icon" />
+<link rel="stylesheet" type="text/css" href="[% themelang %]/css/print.css" />
+[% IF stylesheet %]
+<link rel="stylesheet" type="text/css" href="[% stylesheet %]" />
+[% END %]
+
+<script language="javascript">
+    function printThenClose() {
+        window.print();
+        window.close();
+    }
+</script>
+</head>
+<body onload="printThenClose();">
+<div id="receipt">
+
+[% IF plain %]
+<pre>
+[% slip %]
+</pre>
+[% ELSE %]
+[% slip %]
+[% END %]
+
+[% INCLUDE 'intranet-bottom.inc' %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember-receipt.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember-receipt.tt
deleted file mode 100644
index 4a85ccb..0000000
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember-receipt.tt
+++ /dev/null
@@ -1,76 +0,0 @@
-[% INCLUDE 'doc-head-open.inc' %]
-<title>Print Receipt for [% cardnumber %]</title>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<link rel="shortcut icon" href="[% IF ( IntranetFavicon ) %][% IntranetFavicon %][% ELSE %][% themelang %]/includes/favicon.ico[% END %]" type="image/x-icon" />
- <link rel="stylesheet" type="text/css" href="[% themelang %]/css/print.css" />
-
-<script language="javascript">
-    function printThenClose() {
-        window.print();
-        window.close();
-    }
-</script>
-</head>
-<body onload="printThenClose();">
-
-<div id="receipt">
-
-<h3>[% LibraryName %]</h3>
-[% IF ( branchname ) %][% branchname %]<br />[% END %]
-Checked out to [% firstname %] [% surname %] <br />
-(<a href="/cgi-bin/koha/circ/circulation.pl?findborrower=[% cardnumber %]">[% cardnumber %]</a>)<br />
-
-[% todaysdate %]<br />
-
-[% IF ( quickslip ) %]
-<h4>Checked Out Today</h4>
-[% FOREACH issueloo IN issueloop %]
-[% IF ( issueloo.red ) %][% ELSE %]
-[% IF ( issueloo.today ) %]
-<p><a href="/cgi-bin/koha/catalogue/detail.pl?item=[% issueloo.itemnumber %]&biblionumber=[% issueloo.biblionumber %]&bi=[% issueloo.biblioitemnumber %]">[% issueloo.title |html %]</a><br />
-Barcode: [% issueloo.barcode %]<br />
-Date due: [% issueloo.date_due %]<br /></p>
-    [% END %]
-    [% END %]
-    [% END %]
-
-[% ELSE %]
-<h4>Checked Out</h4>
-[% FOREACH issueloo IN issueloop %]
-[% IF ( issueloo.red ) %][% ELSE %]
-<p><a href="/cgi-bin/koha/catalogue/detail.pl?item=[% issueloo.itemnumber %]&biblionumber=[% issueloo.biblionumber %]&bi=[% issueloo.biblioitemnumber %]">[% issueloo.title |html %]</a><br />
-Barcode: [% issueloo.barcode %]<br />
-Date due: [% issueloo.date_due %]<br /></p>
-    [% END %]
-    [% END %]
-
-[% END %]
-
-[% IF ( quickslip ) %]
-[% ELSE %]
-[% IF ( overdues_exist ) %]
-<h4>Overdues</h4>
-    [% FOREACH issueloo IN issueloop %]
-    [% IF ( issueloo.red ) %]
-<p><a href="/cgi-bin/koha/catalogue/detail.pl?item=[% issueloo.itemnumber %]&biblionumber=[% issueloo.biblionumber %]&bi=[% issueloo.biblioitemnumber %]">[% issueloo.title |html %]</a><br />
-Barcode: [% issueloo.barcode %]<br />
-Date due: [% issueloo.date_due %]</p>
-[% END %]
-[% END %]
-[% END %]
-[% END %]
-
-[% IF ( koha_news_count ) %]
-            <hr><h4 style="text-align: center; font-style:italic;">News</h4>
-                       <!-- [% koha_news_count %] recent news item(s) -->
-            [% FOREACH koha_new IN koha_news %]
-                    <div class="newsitem" id="news[% koha_new.idnew %]"><h5 style="margin-bottom: 1px; margin-top: 1px"><b>[% koha_new.title %]</b></h5>
-                                        <p style="margin-bottom: 1px; margin-top: 1px">[% koha_new.new %]</p>
-                                       <p class="newsfooter" style="font-size: 8pt; font-style:italic; margin-bottom: 1px; margin-top: 1px"> Posted on [% koha_new.newdate %]
-
-</p><hr /></div>
-            [% END %]
-[% END %]
-
-
-[% INCLUDE 'intranet-bottom.inc' %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/letter.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/letter.tt
index 063236e..b64477e 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/letter.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/letter.tt
@@ -10,6 +10,10 @@ $(document).ready(function() {
 		sortList: [[0,0]],
 		headers: { 3: {sorter:false},4: { sorter: false }}
 	}); 
+
+    $('#branch').change(function() {
+            $('#selectlibrary').submit();
+    });
 }); 
 [% IF ( add_form ) %]
 	
@@ -114,7 +118,7 @@ $(document).ready(function() {
 	<div id="yui-main">
 	<div class="yui-b">
 
-	[% IF ( no_op_set ) %]
+[% IF ( no_op_set ) %]
 <div id="toolbar">
 	<script type="text/javascript">
 	//<![CDATA[
@@ -128,6 +132,22 @@ $(document).ready(function() {
 	}
 	//]]>
 	</script>
+
+    [% UNLESS independant_branch %]        
+      <p>
+        <form method="get" action="?" id="selectlibrary">
+            <input type="hidden" name="searchfield" value="[% searchfield %]" />
+        Select a library :
+            <select name="branchcode" id="branch" style="width:20em;">
+                <option value="">All libraries</option>
+            [% FOREACH branchloo IN branchloop %]
+                [% IF ( branchloo.selected ) %]<option value="[% branchloo.value %]" selected="selected">[% branchloo.branchname %]</option>[% ELSE %]<option value="[% branchloo.value %]">[% branchloo.branchname %]</option>[% END %]
+            [% END %]
+            </select>
+        </form>
+      </p>
+    [% END %]        
+
 	<ul class="toolbar">
 	<li><a id="newnotice" href="/cgi-bin/koha/tools/letter.pl?op=add_form">New Notice</a></li>
 </ul></div>
@@ -135,41 +155,71 @@ $(document).ready(function() {
 		[% IF ( search ) %]
 		<p>You Searched for <b>[% searchfield %]</b></p>
 		[% END %]
-		[% IF ( letter ) %]<table id="lettert">
+		[% IF ( letter && !independant_branch) %]
+            [% select_for_copy = BLOCK %]
+            <select name="branchcode">
+                [% FOREACH branchloo IN branchloop %]
+                <option value="[% branchloo.value %]">[% branchloo.branchname %]</option>
+                [% END %]
+            </select>
+            [% END %]
+        [% END %]
+        <table id="lettert">
 		<thead><tr>
+			<th>Branch</th>
 			<th>Module</th>
 			<th>Code</th>
 			<th>Name</th>
 			<th> </th>
 			<th> </th>
+			<th> </th>
 		</tr></thead>
-		<tbody>[% FOREACH lette IN letter %]
-		[% UNLESS ( loop.odd ) %]
+		<tbody>
+    [% FOREACH lette IN letter %]
+        [% can_edit = lette.branchcode || !independant_branch %]
+        [% UNLESS ( loop.odd ) %]
 			<tr class="highlight">
-		[% ELSE %]
+        [% ELSE %]
 			<tr>
-		[% END %]
+        [% END %]
+				<td>[% lette.branchname || "(All libraries)" %]</td>
 				<td>[% lette.module %]</td>
 				<td>[% lette.code %]</td>
 				<td>[% lette.name %]</td>
 				<td>
-					<a href="/cgi-bin/koha/tools/letter.pl?op=add_form&module=[% lette.module %]&code=[% lette.code %]">Edit</a>
+        [% IF can_edit %]
+					<a href="/cgi-bin/koha/tools/letter.pl?op=add_form&branchcode=[% lette.branchcode %]&module=[% lette.module %]&code=[% lette.code %]">Edit</a>
+        [% END %]        
 				</td>
 				<td>
-					[% IF ( lette.protected ) %]
-					-
-					[% ELSE %]
-					<a href="/cgi-bin/koha/tools/letter.pl?op=delete_confirm&module=[% lette.module %]&code=[% lette.code %]">Delete</a>
-					[% END %]
+        [% IF !independant_branch || !lette.branchcode %]
+                    <form method="post" action="?">
+                        <input type="hidden" name="op" value="copy" />
+				        <input type="hidden" name="oldbranchcode" value="[% lette.branchcode %]" />
+                        <input type="hidden" name="module" value="[% lette.module %]" />
+                        <input type="hidden" name="code" value="[% lette.code %]" />
+            [% IF independant_branch %]
+                        <input type="hidden" name="branchcode" value="[% independant_branch %]" />
+            [% ELSE %]
+                        [% select_for_copy %]
+            [% END %]
+                        <input type="submit" value="Copy" />
+                    </form>
+        [% END %]        
+				</td>
+				<td>
+        [% IF !lette.protected && can_edit %]
+					<a href="/cgi-bin/koha/tools/letter.pl?op=delete_confirm&branchcode=[%lette.branchcode %]&module=[% lette.module %]&code=[% lette.code %]">Delete</a>
+        [% END %]
 				</td>
 			</tr>
-		[% END %]</tbody>
+    [% END %]
+        </tbody>
 		</table>
-		[% END %]
+[% END %]
 
-	[% END %]
 	
-	[% IF ( add_form ) %]
+[% IF ( add_form ) %]
 	
 		<form action="/cgi-bin/koha/tools/letter.pl" name="Aform" method="post">
 		<input type="hidden" name="op" value="add_validate" />
@@ -182,6 +232,20 @@ $(document).ready(function() {
 		<fieldset class="rows">
 		<legend>[% IF ( modify ) %]Modify notice[% ELSE %]Add notice[% END %]</legend>
 		<ol>
+				<input type="hidden" name="oldbranchcode" value="[% branchcode %]" />
+            [% IF independant_branch %]
+                <input type="hidden" name="branchcode" value="[% independant_branch %]" />
+            [% ELSE %]
+			<li>
+				<label for="branchcode">Library:</label>
+                <select name="branchcode" id="branch" style="width:20em;">
+                    <option value="">All libraries</option>
+                [% FOREACH branchloo IN branchloop %]
+                    [% IF ( branchloo.selected ) %]<option value="[% branchloo.value %]" selected="selected">[% branchloo.branchname %]</option>[% ELSE %]<option value="[% branchloo.value %]">[% branchloo.branchname %]</option>[% END %]
+                [% END %]
+                </select>
+			</li>
+            [% END %]
 			<li>
 				<label for="module">Koha module:</label>
 				<input type="hidden" name="oldmodule" value="[% module %]" />
@@ -235,6 +299,9 @@ $(document).ready(function() {
 			<label for="name">Name:</label><input type="text" id="name" name="name" size="60" value="[% name %]" />
 		</li>
 		<li>
+			<label for="is_html">HTML Message:</label><input type="checkbox" id="is_html" name="is_html" value="1"[% IF is_html %] checked[% END %] />
+		</li>
+		<li>
 			<label for="title">Message Subject:</label><input type="text" id="title" name="title" size="60" value="[% title %]" />
 		</li>
 		<li>
@@ -253,16 +320,16 @@ $(document).ready(function() {
 		</fieldset>
 		<fieldset class="action"><input type="button" value="Submit" onclick="Check(this.form)" class="button" /></fieldset>
 		</form>
-	[% END %]
+[% END %]
 	
-	[% IF ( add_validate ) %]
+[% IF ( add_validate ) %]
 	Data recorded
 	<form action="[% action %]" method="post">
 	<input type="submit" value="OK" />
 	</form>
-	[% END %]
+[% END %]
 	
-	[% IF ( delete_confirm ) %]
+[% IF ( delete_confirm ) %]
 	<div class="dialog alert"><h3>Delete Notice?</h3>
 	<table>
         <thead>
@@ -290,14 +357,14 @@ $(document).ready(function() {
 				</form>
 		</div>
 
-	[% END %]
+[% END %]
 	
-	[% IF ( delete_confirmed ) %]
+[% IF ( delete_confirmed ) %]
 	Data deleted
 	<form action="[% action %]" method="post">
 	<input type="submit" value="OK" />
 	</form>
-	[% END %]
+[% END %]
 
 </div>
 </div>
diff --git a/members/memberentry.pl b/members/memberentry.pl
index f01261e..195704a 100755
--- a/members/memberentry.pl
+++ b/members/memberentry.pl
@@ -327,10 +327,7 @@ if ((!$nok) and $nodouble and ($op eq 'insert' or $op eq 'save')){
             # if we manage to find a valid email address, send notice 
             if ($emailaddr) {
                 $newdata{emailaddr} = $emailaddr;
-                my $letter = getletter ('members', "ACCTDETAILS:$newdata{'branchcode'}") ;
-                # if $branch notice fails, then email a default notice instead.
-                $letter = getletter ('members', "ACCTDETAILS")  if !$letter;
-                SendAlerts ( 'members' , \%newdata , $letter ) if $letter
+                SendAlerts ( 'members', \%newdata, "ACCTDETAILS" );
             }
         } 
 
diff --git a/members/moremember.pl b/members/moremember.pl
index 9115dd1..6b7a50f 100755
--- a/members/moremember.pl
+++ b/members/moremember.pl
@@ -50,7 +50,6 @@ use C4::Biblio;
 use C4::Reserves;
 use C4::Branch; # GetBranchName
 use C4::Form::MessagingPreferences;
-use C4::NewsChannels; #get slip news
 use List::MoreUtils qw/uniq/;
 use C4::Members::Attributes qw(GetBorrowerAttributes);
 
@@ -473,13 +472,4 @@ $template->param(
     quickslip		  => $quickslip,
 );
 
-#Get the slip news items
-my $all_koha_news   = &GetNewsToDisplay("slip");
-my $koha_news_count = scalar @$all_koha_news;
-
-$template->param(
-    koha_news       => $all_koha_news,
-    koha_news_count => $koha_news_count
-);
-
 output_html_with_http_headers $input, $cookie, $template->output;
diff --git a/members/printslip.pl b/members/printslip.pl
new file mode 100755
index 0000000..eba8d68
--- /dev/null
+++ b/members/printslip.pl
@@ -0,0 +1,89 @@
+#!/usr/bin/perl
+
+# Copyright 2000-2002 Katipo Communications
+# Copyright 2010 BibLibre
+#
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+
+=head1 moremember.pl
+
+ script to do a borrower enquiry/bring up borrower details etc
+ Displays all the details about a borrower
+ written 20/12/99 by chris at katipo.co.nz
+ last modified 21/1/2000 by chris at katipo.co.nz
+ modified 31/1/2001 by chris at katipo.co.nz
+   to not allow items on request to be renewed
+
+ needs html removed and to use the C4::Output more, but its tricky
+
+=cut
+
+use strict;
+#use warnings; FIXME - Bug 2505
+use CGI;
+use C4::Context;
+use C4::Auth;
+use C4::Output;
+use C4::Members;
+use C4::Koha;
+
+#use Smart::Comments;
+#use Data::Dumper;
+
+use vars qw($debug);
+
+BEGIN {
+	$debug = $ENV{DEBUG} || 0;
+}
+
+my $input = new CGI;
+$debug or $debug = $input->param('debug') || 0;
+my $print = $input->param('print');
+my $error = $input->param('error');
+
+# circ staff who process checkouts but can't edit
+# patrons still need to be able to print receipts
+my $flagsrequired = { circulate => "circulate_remaining_permissions" };
+
+my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+    {
+        template_name   => "circ/printslip.tmpl",
+        query           => $input,
+        type            => "intranet",
+        authnotrequired => 0,
+        flagsrequired   => $flagsrequired,
+        debug           => 1,
+    }
+);
+
+my $borrowernumber = $input->param('borrowernumber');
+my $branch=C4::Context->userenv->{'branch'};
+my ($slip, $is_html);
+if (my $letter = IssueSlip ($branch, $borrowernumber, $print eq "qslip")) {
+    $slip = $letter->{content};
+    $is_html = $letter->{is_html};
+}
+
+$template->param(
+    slip => $slip,
+    plain => !$is_html,
+    title => "Print Receipt for $borrowernumber",
+    stylesheet => C4::Context->preference("SlipCSS"),
+    error           => $error,
+);
+
+output_html_with_http_headers $input, $cookie, $template->output;
diff --git a/misc/cronjobs/advance_notices.pl b/misc/cronjobs/advance_notices.pl
index 1fa358f..09c4017 100755
--- a/misc/cronjobs/advance_notices.pl
+++ b/misc/cronjobs/advance_notices.pl
@@ -79,13 +79,10 @@ patrons. It queues them in the message queue, which is processed by
 the process_message_queue.pl cronjob.
 See the comments in the script for directions on changing the script.
 This script has the following parameters :
-	-c Confirm and remove this help & warning
-        -m maximum number of days in advance to send advance notices.
-	-n send No mail. Instead, all mail messages are printed on screen. Usefull for testing purposes.
-        -v verbose
-        -i csv list of fields that get substituted into templates in places
-           of the E<lt>E<lt>items.contentE<gt>E<gt> placeholder.  Defaults to
-           issuedate,title,barcode,author
+    -c Confirm and remove this help & warning
+    -m maximum number of days in advance to send advance notices.
+    -n send No mail. Instead, all mail messages are printed on screen. Usefull for testing purposes.
+    -v verbose
 ENDUSAGE
 
 # Since advance notice options are not visible in the web-interface
@@ -157,8 +154,6 @@ UPCOMINGITEM: foreach my $upcoming ( @$upcoming_dues ) {
         } else {
             my $biblio = C4::Biblio::GetBiblioFromItemNumber( $upcoming->{'itemnumber'} );
             my $letter_type = 'DUE';
-            $letter = C4::Letters::getletter( 'circulation', $letter_type );
-            die "no letter of type '$letter_type' found. Please see sample_notices.sql" unless $letter;
             $sth->execute($upcoming->{'borrowernumber'},$upcoming->{'itemnumber'},'0');
             my $titles = "";
             while ( my $item_info = $sth->fetchrow_hashref()) {
@@ -166,13 +161,14 @@ UPCOMINGITEM: foreach my $upcoming ( @$upcoming_dues ) {
               $titles .= join("\t", at item_info) . "\n";
             }
         
-            $letter = parse_letter( { letter         => $letter,
+            $letter = parse_letter( { letter_code    => $letter_type,
                                       borrowernumber => $upcoming->{'borrowernumber'},
                                       branchcode     => $upcoming->{'branchcode'},
                                       biblionumber   => $biblio->{'biblionumber'},
                                       itemnumber     => $upcoming->{'itemnumber'},
                                       substitute     => { 'items.content' => $titles }
-                                    } );
+                                    } )
+              or die "no letter of type '$letter_type' found. Please see sample_notices.sql";
         }
     } else {
         $borrower_preferences = C4::Members::Messaging::GetMessagingPreferences( { borrowernumber => $upcoming->{'borrowernumber'},
@@ -189,8 +185,6 @@ UPCOMINGITEM: foreach my $upcoming ( @$upcoming_dues ) {
         } else {
             my $biblio = C4::Biblio::GetBiblioFromItemNumber( $upcoming->{'itemnumber'} );
             my $letter_type = 'PREDUE';
-            $letter = C4::Letters::getletter( 'circulation', $letter_type );
-            die "no letter of type '$letter_type' found. Please see sample_notices.sql" unless $letter;
             $sth->execute($upcoming->{'borrowernumber'},$upcoming->{'itemnumber'},$borrower_preferences->{'days_in_advance'});
             my $titles = "";
             while ( my $item_info = $sth->fetchrow_hashref()) {
@@ -198,13 +192,14 @@ UPCOMINGITEM: foreach my $upcoming ( @$upcoming_dues ) {
               $titles .= join("\t", at item_info) . "\n";
             }
         
-            $letter = parse_letter( { letter         => $letter,
+            $letter = parse_letter( { letter_code    => $letter_type,
                                       borrowernumber => $upcoming->{'borrowernumber'},
                                       branchcode     => $upcoming->{'branchcode'},
                                       biblionumber   => $biblio->{'biblionumber'},
                                       itemnumber     => $upcoming->{'itemnumber'},
                                       substitute     => { 'items.content' => $titles }
-                                    } );
+                                    } )
+              or die "no letter of type '$letter_type' found. Please see sample_notices.sql";
         }
     }
 
@@ -250,8 +245,6 @@ PATRON: while ( my ( $borrowernumber, $digest ) = each %$upcoming_digest ) {
 
 
     my $letter_type = 'PREDUEDGST';
-    my $letter = C4::Letters::getletter( 'circulation', $letter_type );
-    die "no letter of type '$letter_type' found. Please see sample_notices.sql" unless $letter;
 
     $sth->execute($borrowernumber,$borrower_preferences->{'days_in_advance'});
     my $titles = "";
@@ -259,12 +252,13 @@ PATRON: while ( my ( $borrowernumber, $digest ) = each %$upcoming_digest ) {
       my @item_info = map { $_ =~ /^date|date$/ ? format_date($item_info->{$_}) : $item_info->{$_} || '' } @item_content_fields;
       $titles .= join("\t", at item_info) . "\n";
     }
-    $letter = parse_letter( { letter         => $letter,
+    my $letter = parse_letter( { letter_code    => $letter_type,
                               borrowernumber => $borrowernumber,
                               substitute     => { count => $count,
                                                   'items.content' => $titles
                                                 }
-                         } );
+                         } )
+      or die "no letter of type '$letter_type' found. Please see sample_notices.sql";
     if ($nomail) {
       local $, = "\f";
       print $letter->{'content'};
@@ -290,20 +284,19 @@ PATRON: while ( my ( $borrowernumber, $digest ) = each %$due_digest ) {
     next PATRON unless $borrower_preferences; # how could this happen?
 
     my $letter_type = 'DUEDGST';
-    my $letter = C4::Letters::getletter( 'circulation', $letter_type );
-    die "no letter of type '$letter_type' found. Please see sample_notices.sql" unless $letter;
     $sth->execute($borrowernumber,'0');
     my $titles = "";
     while ( my $item_info = $sth->fetchrow_hashref()) {
       my @item_info = map { $_ =~ /^date|date$/ ? format_date($item_info->{$_}) : $item_info->{$_} || '' } @item_content_fields;
       $titles .= join("\t", at item_info) . "\n";
     }
-    $letter = parse_letter( { letter         => $letter,
+    my $letter = parse_letter( { letter_code    => $letter_type,
                               borrowernumber => $borrowernumber,
                               substitute     => { count => $count,
                                                   'items.content' => $titles
                                                 }
-                         } );
+                         } )
+      or die "no letter of type '$letter_type' found. Please see sample_notices.sql";
 
     if ($nomail) {
       local $, = "\f";
@@ -323,40 +316,35 @@ PATRON: while ( my ( $borrowernumber, $digest ) = each %$due_digest ) {
 
 =head2 parse_letter
 
-
-
 =cut
 
 sub parse_letter {
     my $params = shift;
-    foreach my $required ( qw( letter borrowernumber ) ) {
+    foreach my $required ( qw( letter_code borrowernumber ) ) {
         return unless exists $params->{$required};
     }
 
-    if ( $params->{'substitute'} ) {
-        while ( my ($key, $replacedby) = each %{$params->{'substitute'}} ) {
-            my $replacefield = "<<$key>>";
-            
-            $params->{'letter'}->{title}   =~ s/$replacefield/$replacedby/g;
-            $params->{'letter'}->{content} =~ s/$replacefield/$replacedby/g;
-        }
-    }
-
-    C4::Letters::parseletter( $params->{'letter'}, 'borrowers',   $params->{'borrowernumber'} );
+    my %table_params = ( 'borrowers' => $params->{'borrowernumber'} );
 
-    if ( $params->{'branchcode'} ) {
-        C4::Letters::parseletter( $params->{'letter'}, 'branches',    $params->{'branchcode'} );
+    if ( my $p = $params->{'branchcode'} ) {
+        $table_params{'branches'} = $p;
     }
-    if ( $params->{'itemnumber'} ) {
-        C4::Letters::parseletter( $params->{'letter'}, 'issues', $params->{'itemnumber'} );
-        C4::Letters::parseletter( $params->{'letter'}, 'items', $params->{'itemnumber'} );
+    if ( my $p = $params->{'itemnumber'} ) {
+        $table_params{'issues'} = $p;
+        $table_params{'items'} = $p;
     }
-    if ( $params->{'biblionumber'} ) {
-        C4::Letters::parseletter( $params->{'letter'}, 'biblio',      $params->{'biblionumber'} );
-        C4::Letters::parseletter( $params->{'letter'}, 'biblioitems', $params->{'biblionumber'} );
+    if ( my $p = $params->{'biblionumber'} ) {
+        $table_params{'biblio'} = $p;
+        $table_params{'biblioitems'} = $p;
     }
 
-    return $params->{'letter'};
+    return C4::Letters::GetPreparedLetter (
+        module => 'circulation',
+        letter_code => $params->{'letter_code'},
+        branchcode => $table_params{'branches'},
+        substitute => $params->{'substitute'},
+        tables     => \%table_params,
+    );
 }
 
 1;
diff --git a/misc/cronjobs/gather_print_notices.pl b/misc/cronjobs/gather_print_notices.pl
index a72d6a6..165b16e 100755
--- a/misc/cronjobs/gather_print_notices.pl
+++ b/misc/cronjobs/gather_print_notices.pl
@@ -39,11 +39,9 @@ use Getopt::Long;
 
 sub usage {
     print STDERR <<USAGE;
-Usage: $0 [ -s STYLESHEET ] OUTPUT_DIRECTORY
+Usage: $0 OUTPUT_DIRECTORY
   Will print all waiting print notices to
   OUTPUT_DIRECTORY/notices-CURRENT_DATE.html .
-  If the filename of a CSS stylesheet is specified with -s, the contents of that
-  file will be included in the HTML.
 USAGE
     exit $_[0];
 }
@@ -51,7 +49,6 @@ USAGE
 my ( $stylesheet, $help );
 
 GetOptions(
-    's:s' => \$stylesheet,
     'h|help' => \$help,
 ) || usage( 1 );
 
@@ -71,16 +68,9 @@ exit unless( @messages );
 open OUTPUT, '>', File::Spec->catdir( $output_directory, "holdnotices-" . $today->output( 'iso' ) . ".html" );
 
 my $template = C4::Templates::gettemplate( 'batch/print-notices.tmpl', 'intranet', new CGI );
-my $stylesheet_contents = '';
-
-if ($stylesheet) {
-  open STYLESHEET, '<', $stylesheet;
-  while ( <STYLESHEET> ) { $stylesheet_contents .= $_ }
-  close STYLESHEET;
-}
 
 $template->param(
-    stylesheet => $stylesheet_contents,
+    stylesheet => C4::Context->preference("NoticeCSS"),
     today => $today->output(),
     messages => \@messages,
 );
diff --git a/misc/cronjobs/overdue_notices.pl b/misc/cronjobs/overdue_notices.pl
index 37774b5..a8544c6 100755
--- a/misc/cronjobs/overdue_notices.pl
+++ b/misc/cronjobs/overdue_notices.pl
@@ -459,17 +459,7 @@ END_SQL
                     $longest_issue ) = $sth->fetchrow )
             {
                 $verbose and warn "borrower $firstname, $lastname ($borrowernumber) has $itemcount items triggering level $i.";
-    
-                my $letter = C4::Letters::getletter( 'circulation', $overdue_rules->{"letter$i"} );
-
-                unless ($letter) {
-                    $verbose and warn "Message '$overdue_rules->{letter$i}' content not found";
-    
-                    # might as well skip while PERIOD, no other borrowers are going to work.
-                    # FIXME : Does this mean a letter must be defined in order to trigger a debar ?
-                    next PERIOD;
-                }
-    
+   
                 if ( $overdue_rules->{"debarred$i"} ) {
     
                     #action taken is debarring
@@ -494,11 +484,12 @@ END_SQL
                     my @item_info = map { $_ =~ /^date|date$/ ? format_date( $item_info->{$_} ) : $item_info->{$_} || '' } @item_content_fields;
                     $titles .= join("\t", @item_info) . "\n";
                     $itemcount++;
-                    push @items, { itemnumber => $item_info->{'itemnumber'}, biblionumber => $item_info->{'biblionumber'} };
+                    push @items, $item_info;
                 }
                 $sth2->finish;
-                $letter = parse_letter(
-                    {   letter          => $letter,
+
+                my $letter = parse_letter(
+                    {   letter_code     => $overdue_rules->{"letter$i"},
                         borrowernumber  => $borrowernumber,
                         branchcode      => $branchcode,
                         items           => \@items,
@@ -508,6 +499,13 @@ END_SQL
                                            }
                     }
                 );
+                unless ($letter) {
+                    $verbose and warn "Message '$overdue_rules->{letter$i}' content not found";
+    
+                    # might as well skip while PERIOD, no other borrowers are going to work.
+                    # FIXME : Does this mean a letter must be defined in order to trigger a debar ?
+                    next PERIOD;
+                }
                 
                 if ( $exceededPrintNoticesMaxLines ) {
                   $letter->{'content'} .= "List too long for form; please check your account online for a complete list of your overdue items.";
@@ -642,53 +640,55 @@ substituted keys and values.
 
 =cut
 
-sub parse_letter { # FIXME: this code should probably be moved to C4::Letters:parseletter
+sub parse_letter {
     my $params = shift;
-    foreach my $required (qw( letter borrowernumber )) {
+    foreach my $required (qw( letter_code borrowernumber )) {
         return unless exists $params->{$required};
     }
 
-   my $todaysdate = C4::Dates->new()->output("syspref");
-   $params->{'letter'}->{title}   =~ s/<<today>>/$todaysdate/g;
-   $params->{'letter'}->{content} =~ s/<<today>>/$todaysdate/g;
+    my $substitute = $params->{'substitute'} || {};
+    $substitute->{today} ||= C4::Dates->new()->output("syspref");
 
-    if ( $params->{'substitute'} ) {
-        while ( my ( $key, $replacedby ) = each %{ $params->{'substitute'} } ) {
-            my $replacefield = "<<$key>>";
-            $params->{'letter'}->{title}   =~ s/$replacefield/$replacedby/g;
-            $params->{'letter'}->{content} =~ s/$replacefield/$replacedby/g;
-        }
+    my %tables = ( 'borrowers' => $params->{'borrowernumber'} );
+    if ( my $p = $params->{'branchcode'} ) {
+        $tables{'branches'} = $p;
     }
 
-    $params->{'letter'} = C4::Letters::parseletter( $params->{'letter'}, 'borrowers', $params->{'borrowernumber'} );
-
-    if ( $params->{'branchcode'} ) {
-        $params->{'letter'} = C4::Letters::parseletter( $params->{'letter'}, 'branches', $params->{'branchcode'} );
+    my $currency_format;
+    if ($params->{'letter'}->{'content'} =~ m/<fine>(.*)<\/fine>/o) { # process any fine tags...
+        $currency_format = $1;
+        $params->{'letter'}->{'content'} =~ s/<fine>.*<\/fine>/<<item.fine>>/o;
     }
 
-    if ( $params->{'items'} ) {
+    my @item_tables;
+    if ( my $i = $params->{'items'} ) {
         my $item_format = '';
-        PROCESS_ITEMS:
-        while (scalar(@{$params->{'items'}}) > 0) {
-            my $item = shift @{$params->{'items'}};
+        foreach my $item (@$i) {
             my $fine = GetFine($item->{'itemnumber'}, $params->{'borrowernumber'});
             if (!$item_format) {
                 $params->{'letter'}->{'content'} =~ m/(<item>.*<\/item>)/;
                 $item_format = $1;
             }
-            if ($params->{'letter'}->{'content'} =~ m/<fine>(.*)<\/fine>/) { # process any fine tags...
-                my $formatted_fine = currency_format("$1", "$fine", FMT_SYMBOL);
-                $params->{'letter'}->{'content'} =~ s/<fine>.*<\/fine>/$formatted_fine/;
-            }
-            $params->{'letter'} = C4::Letters::parseletter( $params->{'letter'}, 'biblio',      $item->{'biblionumber'} );
-            $params->{'letter'} = C4::Letters::parseletter( $params->{'letter'}, 'biblioitems', $item->{'biblionumber'} );
-            $params->{'letter'} = C4::Letters::parseletter( $params->{'letter'}, 'items', $item->{'itemnumber'} );
-            $params->{'letter'}->{'content'} =~ s/(<item>.*<\/item>)/$1\n$item_format/ if scalar(@{$params->{'items'}} > 0);
 
+            $item->{'fine'} = currency_format($currency_format, "$fine", FMT_SYMBOL)
+              if $currency_format;
+
+            push @item_tables, {
+                'biblio' => $item->{'biblionumber'},
+                'biblioitems' => $item->{'biblionumber'},
+                'items' => $item,
+            };
         }
     }
-    $params->{'letter'}->{'content'} =~ s/<\/{0,1}?item>//g; # strip all remaining item tags...
-    return $params->{'letter'};
+
+    return C4::Letters::GetPreparedLetter (
+        module => 'circulation',
+        letter_code => $params->{'letter_code'},
+        branchcode => $params->{'branchcode'},
+        tables => \%tables,
+        substitute => $substitute,
+        repeat => { item => \@item_tables },
+    );
 }
 
 =head2 prepare_letter_for_printing
diff --git a/t/db_dependent/lib/KohaTest/Letters.pm b/t/db_dependent/lib/KohaTest/Letters.pm
index 97d58fb..f2d7b0d 100644
--- a/t/db_dependent/lib/KohaTest/Letters.pm
+++ b/t/db_dependent/lib/KohaTest/Letters.pm
@@ -12,13 +12,12 @@ sub testing_class { 'C4::Letters' };
 
 sub methods : Test( 1 ) {
     my $self = shift;
-    my @methods = qw( getletter
-                      addalert
+    my @methods = qw( addalert
                       delalert
                       getalert
                       findrelatedto
                       SendAlerts
-                      parseletter
+                      GetPreparedLetter
                 );
     
     can_ok( $self->testing_class, @methods );    
diff --git a/t/db_dependent/lib/KohaTest/Letters/GetLetter.pm b/t/db_dependent/lib/KohaTest/Letters/GetLetter.pm
index 76b6ab4..53e5439 100644
--- a/t/db_dependent/lib/KohaTest/Letters/GetLetter.pm
+++ b/t/db_dependent/lib/KohaTest/Letters/GetLetter.pm
@@ -10,7 +10,7 @@ use Test::More;
 sub GetLetter : Test( 6 ) {
     my $self = shift;
 
-    my $letter = getletter( 'circulation', 'ODUE' );
+    my $letter = getletter( 'circulation', 'ODUE', '' );
 
     isa_ok( $letter, 'HASH' )
       or diag( Data::Dumper->Dump( [ $letter ], [ 'letter' ] ) );
@@ -21,7 +21,6 @@ sub GetLetter : Test( 6 ) {
     ok( exists $letter->{'name'}, 'name' );
     ok( exists $letter->{'title'}, 'title' );
 
-
 }
 
 1;
diff --git a/t/db_dependent/lib/KohaTest/Members.pm b/t/db_dependent/lib/KohaTest/Members.pm
index 5646be1..dfde7da 100644
--- a/t/db_dependent/lib/KohaTest/Members.pm
+++ b/t/db_dependent/lib/KohaTest/Members.pm
@@ -52,6 +52,7 @@ sub methods : Test( 1 ) {
                       GetBorrowersWhoHaveNeverBorrowed 
                       GetBorrowersWithIssuesHistoryOlderThan 
                       GetBorrowersNamesAndLatestIssue 
+                      IssueSlip
                 );
     
     can_ok( $self->testing_class, @methods );    
diff --git a/t/db_dependent/lib/KohaTest/Print.pm b/t/db_dependent/lib/KohaTest/Print.pm
index 02fd5fb..d35ab34 100644
--- a/t/db_dependent/lib/KohaTest/Print.pm
+++ b/t/db_dependent/lib/KohaTest/Print.pm
@@ -12,10 +12,7 @@ sub testing_class { 'C4::Print' };
 
 sub methods : Test( 1 ) {
     my $self = shift;
-    my @methods = qw( remoteprint
-                      printreserve 
-                      printslip
-                );
+    my @methods = qw( printslip );
     
     can_ok( $self->testing_class, @methods );    
 }
diff --git a/t/db_dependent/lib/KohaTest/Reserves.pm b/t/db_dependent/lib/KohaTest/Reserves.pm
index 5317029..70a96fc 100644
--- a/t/db_dependent/lib/KohaTest/Reserves.pm
+++ b/t/db_dependent/lib/KohaTest/Reserves.pm
@@ -32,6 +32,7 @@ sub methods : Test( 1 ) {
                        GetReserveInfo 
                        _FixPriority 
                        _Findgroupreserve 
+                       ReserveSlip
                 );
     
     can_ok( $self->testing_class, @methods );    
diff --git a/tools/letter.pl b/tools/letter.pl
index f5dc0c6..7cfca55 100755
--- a/tools/letter.pl
+++ b/tools/letter.pl
@@ -46,14 +46,34 @@ use CGI;
 use C4::Auth;
 use C4::Context;
 use C4::Output;
+use C4::Branch; # GetBranches
 
-# letter_exists($module, $code)
-# - return true if a letter with the given $module and $code exists
+# _letter_from_where($branchcode,$module, $code)
+# - return FROM WHERE clause and bind args for a letter
+sub _letter_from_where {
+    my ($branchcode, $module, $code) = @_;
+    my $sql = q{FROM letter WHERE branchcode = ? AND module = ? AND code = ?};
+    my @args = ($branchcode || '', $module, $code);
+# Mysql is retarded. cause branchcode is part of the primary key it cannot be null. How does that
+# work with foreign key constraint I wonder...
+
+#   if ($branchcode) {
+#       $sql .= " AND branchcode = ?";
+#       push @args, $branchcode;
+#   } else {
+#       $sql .= " AND branchcode IS NULL";
+#   }
+
+    return ($sql, \@args);
+}
+
+# letter_exists($branchcode,$module, $code)
+# - return true if a letter with the given $branchcode, $module and $code exists
 sub letter_exists {
-    my ($module, $code) = @_;
+    my ($sql, $args) = _letter_from_where(@_);
     my $dbh = C4::Context->dbh;
-    my $letters = $dbh->selectall_arrayref(q{SELECT name FROM letter WHERE module = ? AND code = ?}, undef, $module, $code);
-    return @{$letters};
+    my $letter = $dbh->selectrow_hashref("SELECT * $sql", undef, @$args);
+    return $letter;
 }
 
 # $protected_letters = protected_letters()
@@ -67,14 +87,12 @@ sub protected_letters {
 my $input       = new CGI;
 my $searchfield = $input->param('searchfield');
 my $script_name = '/cgi-bin/koha/tools/letter.pl';
+my $branchcode  = $input->param('branchcode');
 my $code        = $input->param('code');
 my $module      = $input->param('module');
 my $content     = $input->param('content');
-my $op          = $input->param('op');
+my $op          = $input->param('op') || '';
 my $dbh = C4::Context->dbh;
-if (!defined $module ) {
-    $module = q{};
-}
 
 my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
     {
@@ -87,32 +105,38 @@ my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
     }
 );
 
-if (!defined $op) {
-    $op = q{}; # silence errors from eq
-}
+my $my_branch = C4::Context->preference("IndependantBranches")
+  ?  C4::Context->userenv()->{'branch'}
+  : undef;
 # we show only the TMPL_VAR names $op
 
 $template->param(
+    independant_branch => $my_branch,
 	script_name => $script_name,
 	action => $script_name
 );
 
+if ($op eq 'copy') {
+    add_copy();
+    $op = 'add_form';
+}
+
 if ($op eq 'add_form') {
-    add_form($module, $code);
+    add_form($branchcode, $module, $code);
 }
 elsif ( $op eq 'add_validate' ) {
     add_validate();
     $op = q{}; # next operation is to return to default screen
 }
 elsif ( $op eq 'delete_confirm' ) {
-    delete_confirm($module, $code);
+    delete_confirm($branchcode, $module, $code);
 }
 elsif ( $op eq 'delete_confirmed' ) {
-    delete_confirmed($module, $code);
+    delete_confirmed($branchcode, $module, $code);
     $op = q{}; # next operation is to return to default screen
 }
 else {
-    default_display($searchfield);
+    default_display($branchcode,$searchfield);
 }
 
 # Do this last as delete_confirmed resets
@@ -125,23 +149,21 @@ if ($op) {
 output_html_with_http_headers $input, $cookie, $template->output;
 
 sub add_form {
-    my ($module, $code ) = @_;
+    my ($branchcode,$module, $code ) = @_;
 
     my $letter;
     # if code has been passed we can identify letter and its an update action
     if ($code) {
-        $letter = $dbh->selectrow_hashref(q{SELECT module, code, name, title, content FROM letter WHERE module=? AND code=?},
-            undef, $module, $code);
+        $letter = letter_exists($branchcode,$module, $code);
+    }
+    if ($letter) {
         $template->param( modify => 1 );
         $template->param( code   => $letter->{code} );
     }
     else { # initialize the new fields
         $letter = {
-            module  => $module,
-            code    => q{},
-            name    => q{},
-            title   => q{},
-            content => q{},
+            branchcode => $branchcode,
+            module     => $module,
         };
         $template->param( adding => 1 );
     }
@@ -173,14 +195,20 @@ sub add_form {
             {value => q{},             text => '---ITEMS---'  },
             {value => 'items.content', text => 'items.content'},
             add_fields('issues','borrowers');
+        if ($module eq 'circulation') {
+            push @{$field_selection}, add_fields('opac_news');
+        }
     }
 
     $template->param(
-        name    => $letter->{name},
-        title   => $letter->{title},
-        content => $letter->{content},
-        module  => $module,
-        $module => 1,
+        branchcode => $letter->{branchcode},
+        name       => $letter->{name},
+        is_html    => $letter->{is_html},
+        title      => $letter->{title},
+        content    => $letter->{content},
+        module     => $module,
+        $module    => 1,
+        branchloop => _branchloop($branchcode),
         SQLfieldname => $field_selection,
     );
     return;
@@ -188,37 +216,56 @@ sub add_form {
 
 sub add_validate {
     my $dbh        = C4::Context->dbh;
-    my $module     = $input->param('module');
-    my $oldmodule  = $input->param('oldmodule');
-    my $code       = $input->param('code');
-    my $name       = $input->param('name');
-    my $title      = $input->param('title');
-    my $content    = $input->param('content');
-    if (letter_exists($oldmodule, $code)) {
+    my $oldbranchcode = $input->param('oldbranchcode');
+    my $branchcode    = $input->param('branchcode') || '';
+    my $module        = $input->param('module');
+    my $oldmodule     = $input->param('oldmodule');
+    my $code          = $input->param('code');
+    my $name          = $input->param('name');
+    my $is_html       = $input->param('is_html');
+    my $title         = $input->param('title');
+    my $content       = $input->param('content');
+    if (letter_exists($oldbranchcode,$oldmodule, $code)) {
         $dbh->do(
-            q{UPDATE letter SET module = ?, code = ?, name = ?, title = ?, content = ? WHERE module = ? AND code = ?},
+            q{UPDATE letter SET branchcode = ?, module = ?, name = ?, is_html = ?, title = ?, content = ? WHERE branchcode = ? AND module = ? AND code = ?},
             undef,
-            $module, $code, $name, $title, $content,
-            $oldmodule, $code
+            $branchcode, $module, $name, $is_html || 0, $title, $content,
+            $oldbranchcode, $oldmodule, $code
         );
     } else {
         $dbh->do(
-            q{INSERT INTO letter (module,code,name,title,content) VALUES (?,?,?,?,?)},
+            q{INSERT INTO letter (branchcode,module,code,name,is_html,title,content) VALUES (?,?,?,?,?,?,?)},
             undef,
-            $module, $code, $name, $title, $content
+            $branchcode, $module, $code, $name, $is_html || 0, $title, $content
         );
     }
     # set up default display
-    default_display();
-    return;
+    default_display($branchcode);
+}
+
+sub add_copy {
+    my $dbh        = C4::Context->dbh;
+    my $oldbranchcode = $input->param('oldbranchcode');
+    my $branchcode    = $input->param('branchcode');
+    my $module        = $input->param('module');
+    my $code          = $input->param('code');
+
+    return if letter_exists($branchcode,$module, $code);
+
+    my $old_letter = letter_exists($oldbranchcode,$module, $code);
+
+    $dbh->do(
+        q{INSERT INTO letter (branchcode,module,code,name,is_html,title,content) VALUES (?,?,?,?,?,?,?)},
+        undef,
+        $branchcode, $module, $code, $old_letter->{name}, $old_letter->{is_html}, $old_letter->{title}, $old_letter->{content}
+    );
 }
 
 sub delete_confirm {
-    my ($module, $code) = @_;
+    my ($branchcode, $module, $code) = @_;
     my $dbh = C4::Context->dbh;
-    my $letter = $dbh->selectrow_hashref(q|SELECT  name FROM letter WHERE module = ? AND code = ?|,
-        { Slice => {} },
-        $module, $code);
+    my $letter = letter_exists($branchcode, $module, $code);
+    $template->param( branchcode => $branchcode );
     $template->param( code => $code );
     $template->param( module => $module);
     $template->param( name => $letter->{name});
@@ -226,40 +273,54 @@ sub delete_confirm {
 }
 
 sub delete_confirmed {
-    my ($module, $code) = @_;
+    my ($branchcode, $module, $code) = @_;
+    my ($sql, $args) = _letter_from_where($branchcode, $module, $code);
     my $dbh    = C4::Context->dbh;
-    $dbh->do('DELETE FROM letter WHERE module=? AND code=?',{},$module,$code);
+    $dbh->do("DELETE $sql", undef, @$args);
     # setup default display for screen
-    default_display();
+    default_display($branchcode);
     return;
 }
 
 sub retrieve_letters {
-    my $searchstring = shift;
+    my ($branchcode, $searchstring) = @_;
+
+    $branchcode = $my_branch if $branchcode && $my_branch;
+
     my $dbh = C4::Context->dbh;
-    if ($searchstring) {
-        if ($searchstring=~m/(\S+)/) {
-            $searchstring = $1 . q{%};
-            return $dbh->selectall_arrayref('SELECT module, code, name FROM letter WHERE code LIKE ? ORDER BY module, code',
-                { Slice => {} }, $searchstring);
-        }
+    my ($sql, @where, @args);
+    $sql = "SELECT branchcode, module, code, name, branchname
+            FROM letter
+            LEFT OUTER JOIN branches USING (branchcode)";
+    if ($searchstring && $searchstring=~m/(\S+)/) {
+        $searchstring = $1 . q{%};
+        push @where, 'code LIKE ?';
+        push @args, $searchstring;
     }
-    else {
-        return $dbh->selectall_arrayref('SELECT module, code, name FROM letter ORDER BY module, code', { Slice => {} });
+    elsif ($branchcode) {
+        push @where, 'branchcode = ?';
+        push @args, $branchcode || '';
     }
-    return;
+    elsif ($my_branch) {
+        push @where, "(branchcode = ? OR branchcode = '')";
+        push @args, $my_branch;
+    }
+
+    $sql .= " WHERE ".join(" AND ", @where) if @where;
+    $sql .= " ORDER BY module, code, branchcode";
+#   use Data::Dumper; die Dumper($sql, \@args);
+    return $dbh->selectall_arrayref($sql, { Slice => {} }, @args);
 }
 
 sub default_display {
-    my $searchfield = shift;
-    my $results;
+    my ($branchcode, $searchfield) = @_;
+
     if ( $searchfield  ) {
         $template->param( search      => 1 );
         $template->param( searchfield => $searchfield );
-        $results = retrieve_letters($searchfield);
-    } else {
-        $results = retrieve_letters();
     }
+    my $results = retrieve_letters($branchcode,$searchfield);
+
     my $loop_data = [];
     my $protected_letters = protected_letters();
     foreach my $row (@{$results}) {
@@ -267,8 +328,27 @@ sub default_display {
         push @{$loop_data}, $row;
 
     }
-    $template->param( letter => $loop_data );
-    return;
+
+    $template->param(
+        letter => $loop_data,
+        branchloop => _branchloop($branchcode),
+    );
+}
+
+sub _branchloop {
+    my ($branchcode) = @_;
+
+    my $branches = GetBranches();
+    my @branchloop;
+    for my $thisbranch (sort { $branches->{$a}->{branchname} cmp $branches->{$b}->{branchname} } keys %$branches) {
+        push @branchloop, {
+            value      => $thisbranch,
+            selected   => $branchcode && $thisbranch eq $branchcode,
+            branchname => $branches->{$thisbranch}->{'branchname'},
+        };
+    }
+
+    return \@branchloop;
 }
 
 sub add_fields {
-- 
1.6.5




More information about the Patches mailing list