From 2541e390bec8bd67dc6f8d866c4c7df42babaea0 Mon Sep 17 00:00:00 2001
From: "A. Sassmannshausen" <alex.sassmannshausen@ptfs-europe.com>
Date: Fri, 4 Apr 2014 09:55:07 +0000
Subject: [PATCH] Bug 5670: Housebound Readers Module

New module to handle management of circulation to Housebound readers.

- Ability to record users as housebound (HB category), volunteers
  (combined deliverers and choosers, VOL category), deliverers (DELIV
  category) choosers (CHO category).
- Ability to link users to individual delivery runs.
- Ability to check on issuing to housebound users flagging
  if title has previously been issued to this user, to aid choosers.
- 'Delivery Days' and 'Delivery Frequencies' are customizable through
  authorised values ('Day' and 'Frequency' categories respectively.

Sponsored by Halton Borough Council

* C4/Auth.pm: Add check: useHouseboundModule.
* C4/Circulation.pm: use Housebound to check for previous issue.
* koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc: add
  Housebound menu if appropriate.
* koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt: add
  'previous checkout' warning.
* C4/Housebound.pm: new.
* koha-tmpl/intranet-tmpl/prog/en/modules/members/housebound.tt: new.
* koha-tmpl/intranet-tmpl/prog/en/modules/members/houseboundedit.tt: new.
* koha-tmpl/intranet-tmpl/prog/en/modules/members/houseboundinstances.tt: new.
* members/housebound.pl: new.
* members/houseboundedit.pl: new.
* members/houseboundinstances.pl: new.
* installer/data/mysql/atomicupdate/housebound_tables.sql: new.
  - create 'housebound', 'housebound_instance' tables,
  - create 'HB', 'VOL', 'DELIV', 'CHO' borrower categories,
  - create sample 'Day', 'Frequency' authorised values,
  - add 'useHouseboundModule', 'useHouseboundCheckPrevious' syspref.
* t/Housebound.t: new.

Test plan:
- Apply patch.
- Run atomic update script.
- Optionally, run Unit Tests (t/Housebound.t)
- Create at least 1 of each: 'HB', 'VOL', 'DELIV', 'CHO' borrower.
- Optionally, add additional authorised values of 'Day' and
  'Frequency' categories.
- Verify 'useHouseboundModule' & 'useHouseboundCheckPrevious' sysprefs
  are on.
- On the page of the 'HB' borrower, open the new 'Housebound' menu.
  - create housebound profile
    + ensure Day and Frequency values are from correct authorised
      values
  - create 'housebound instances' (deliveries)
    + ensure chooser/deliverer lists are populated with VOL, DELIV,
      CHO borrowers
  - edit the profile/instances.
  - delete the profile/instances.
- For a borrower with a housebound profile, issue an item, then
  - return that item
  - attempt to re-issue that item (you should get a warning)
- Switch off 'useHouseboundCheckPrevious', repeat last test
  - the warning should not appear
- Switch off 'useHouseboundModule'
  - the Housebound menu should disappear
  - the issue warnings should no longer appear.

Largely based on prior work from Mark Gavillet and Colin Campbell.
---
 C4/Auth.pm                                         |    1 +
 C4/Circulation.pm                                  |   12 +
 C4/Housebound.pm                                   |  377 ++++++++++++++++++++
 .../data/mysql/atomicupdate/housebound_tables.sql  |   41 +++
 .../intranet-tmpl/prog/en/includes/circ-menu.inc   |    3 +
 .../prog/en/modules/circ/circulation.tt            |    4 +
 .../prog/en/modules/members/housebound.tt          |   69 ++++
 .../prog/en/modules/members/houseboundedit.tt      |  102 ++++++
 .../prog/en/modules/members/houseboundinstances.tt |  133 +++++++
 members/housebound.pl                              |   91 +++++
 members/houseboundedit.pl                          |  114 ++++++
 members/houseboundinstances.pl                     |  138 +++++++
 t/Housebound.t                                     |  292 +++++++++++++++
 13 files changed, 1377 insertions(+)
 create mode 100644 C4/Housebound.pm
 create mode 100644 installer/data/mysql/atomicupdate/housebound_tables.sql
 create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/members/housebound.tt
 create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/members/houseboundedit.tt
 create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/members/houseboundinstances.tt
 create mode 100755 members/housebound.pl
 create mode 100755 members/houseboundedit.pl
 create mode 100755 members/houseboundinstances.pl
 create mode 100755 t/Housebound.t

diff --git a/C4/Auth.pm b/C4/Auth.pm
index 44edf67..d47ee0f 100644
--- a/C4/Auth.pm
+++ b/C4/Auth.pm
@@ -328,6 +328,7 @@ sub get_template_and_user {
     );
     if ( $in->{'type'} eq "intranet" ) {
         $template->param(
+            useHouseboundModule         => C4::Context->preference("useHouseboundModule"),
             AmazonCoverImages           => C4::Context->preference("AmazonCoverImages"),
             AutoLocation                => C4::Context->preference("AutoLocation"),
             "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
diff --git a/C4/Circulation.pm b/C4/Circulation.pm
index f8b204a..2ad74d8 100644
--- a/C4/Circulation.pm
+++ b/C4/Circulation.pm
@@ -58,6 +58,8 @@ use Date::Calc qw(
   Day_of_Week
   Add_Delta_Days
 );
+use C4::Housebound qw( CheckPrevIssue );
+
 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
 
 BEGIN {
@@ -840,6 +842,16 @@ sub CanBookBeIssued {
         }
     }
 
+    # If Housebound patron and useHouseboundCheckPrev syspref is ON
+    # check for previous issue of item to patron.
+    if ( C4::Context->preference('useHouseboundCheckPrevious') == 1
+         && $borrower->{categorycode} eq 'HB' )
+    {
+        $needsconfirmation{HOUSEBOUNDPREVISSUE} = 1
+          if CheckPrevIssue( $borrower->{borrowernumber},
+                             $item->{biblionumber} );
+    }
+
     #
     # ITEM CHECKING
     #
diff --git a/C4/Housebound.pm b/C4/Housebound.pm
new file mode 100644
index 0000000..879334b
--- /dev/null
+++ b/C4/Housebound.pm
@@ -0,0 +1,377 @@
+package C4::Housebound;
+
+# Copyright 2011 Mark Gavillet
+# Copyright 2014 PTFS-Europe Ltd
+#
+# 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 3 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.
+
+use strict;
+use warnings;
+use C4::Context;
+use base qw(Exporter);
+
+our $VERSION = 3.02;
+our @EXPORT_OK  = qw(
+  GetHouseboundDetails
+  CreateHouseboundDetails
+  UpdateHouseboundDetails
+  GetCurrentHouseboundInstanceList
+  GetHouseboundInstanceDetails
+  UpdateHouseboundInstanceDetails
+  CreateHouseboundInstanceDetails
+  DeleteHouseboundInstanceDetails
+  GetVolunteerNameAndID
+  GetVolunteerList
+  GetChooserList
+  GetDelivererList
+  CheckPrevIssue
+);
+
+=head1 NAME
+
+C4::Housebound - Koha housebound patrons module.
+
+=head1 SYNOPSIS
+
+use C4::Housebound;
+
+=head1 DESCRIPTION
+
+This module contains routines for adding, modifying and deleting
+housebound details for patrons, and instances of item delivery by
+volunteers.
+
+=head1 FUNCTIONS
+
+=head2 GetHouseboundDetails
+
+  $housebound = GetHouseboundDetails( $borrowernumber );
+
+Return a hashref containing the housebound table's row associated with
+$BORROWERNUMBER or undef.
+
+=cut
+
+sub GetHouseboundDetails {
+    my ($borrowernumber) = @_;
+    if ($borrowernumber) {
+        my $dbh = C4::Context->dbh;
+        return $dbh->selectrow_hashref(
+            'select * from housebound where borrowernumber=?',
+            {}, $borrowernumber );
+    }
+    return;
+}
+
+=head2 CreateHouseboundDetails
+
+  CreateHouseboundDetails( $borrowernumber, $day, $frequency,
+                           $Itype_quant, $Item_subject, $Item_authors,
+                           $referral, $notes );
+
+Create a new entry in the housebound table containing our arguments.
+The return value is undef.
+
+=cut
+
+sub CreateHouseboundDetails {
+    my ( $borrowernumber, $day, $frequency, $Itype_quant, $Item_subject,
+        $Item_authors, $referral, $notes )
+      = @_;
+    if ($borrowernumber) {
+        my $dbh = C4::Context->dbh;
+        my $sth = $dbh->prepare(
+'insert into housebound (day, frequency, borrowernumber, Itype_quant, Item_subject, Item_authors, referral, notes) values (?,?,?,?,?,?,?,?)'
+        );
+        $sth->execute( $day, $frequency, $borrowernumber, $Itype_quant,
+            $Item_subject, $Item_authors, $referral, $notes );
+    }
+    return;
+}
+
+=head2 UpdateHouseboundDetails
+
+  UpdateHouseboundDetails( $hbnumber, $borrowernumber, $day,
+                           $frequency, $Itype_quant, $Item_subject,
+                           $Item_authors, $referral, $notes );
+
+Update housebound table's row matching $hbnumber with our arguments.
+The return value is undef.
+
+=cut
+
+sub UpdateHouseboundDetails {
+    my (
+        $hbnumber,     $borrowernumber, $day,
+        $frequency,    $Itype_quant,    $Item_subject,
+        $Item_authors, $referral,       $notes
+    ) = @_;
+    if ($hbnumber) {
+        my $dbh = C4::Context->dbh;
+        my $sth = $dbh->prepare(
+'update housebound set day=?,frequency=?,borrowernumber=?,Itype_quant=?,Item_subject=?,Item_authors=?,referral=?,notes=? where hbnumber=?'
+        );
+        $sth->execute(
+            $day,         $frequency,    $borrowernumber,
+            $Itype_quant, $Item_subject, $Item_authors,
+            $referral,    $notes,        $hbnumber
+        );
+    }
+    return;
+}
+
+=head2 GetCurrentHouseboundInstanceList
+
+  $hb_instances = GetCurrentHouseboundInstanceList( $borrowernumber );
+
+Return an arrayref containing all Housebound_Instances associated with
+$BORROWERNUMBER, or undef.
+
+=cut
+
+sub GetCurrentHouseboundInstanceList {
+    my ($borrowernumber) = @_;
+    if ($borrowernumber) {
+        my $dbh = C4::Context->dbh;
+        my $sql =<<'END_SQL';
+SELECT housebound_instance . * , concat( volunteer.firstname, ' ', volunteer.surname ) AS vol,
+ concat(chooser.firstname, ' ', chooser.surname) as cho,
+ concat(deliverer.firstname, ' ', deliverer.surname) as del
+ FROM housebound_instance
+ left JOIN borrowers volunteer ON volunteer.borrowernumber = housebound_instance.volunteer
+ left join borrowers chooser on chooser.borrowernumber = housebound_instance.chooser
+ left join borrowers deliverer on deliverer.borrowernumber = housebound_instance.deliverer
+ where housebound_instance.borrowernumber=?
+ order by housebound_instance.dmy desc
+END_SQL
+          return $dbh->selectall_arrayref( $sql, { Slice => {} },
+            $borrowernumber );
+    }
+    return;
+}
+
+=head2 GetVolunteerNameAndID
+
+  $volunteer = GetVolunteerNameAndID( $borrowernumber );
+
+Return a hashref containing details of the volunteer identified by
+$BORROWERNUMBER, or undef.
+
+=cut
+
+sub GetVolunteerNameAndID {
+    my ($borrowernumber) = @_;
+    if ($borrowernumber) {
+        my $dbh = C4::Context->dbh;
+        return $dbh->selectrow_hashref(
+'select borrowernumber,title,firstname,surname from borrowers where borrowernumber=?',
+            {}, $borrowernumber
+        );
+    }
+    return;
+}
+
+=head2 GetVolunteerList
+
+  $volunteers = GetVolunteerList();
+
+Return an arrayref containing borrowernumbers and names of all known
+volunteers, or undef.
+
+=cut
+
+sub GetVolunteerList {
+    return GetHouseboundList("VOL");
+}
+
+=head2 GetDelivererList
+
+  $deliverers = GetDelivererList();
+
+Return an arrayref containing borrowernumbers and names of all known
+deliverers, or undef.
+
+=cut
+
+sub GetDelivererList {
+    return GetHouseboundList("DELIV");
+}
+
+=head2 GetChooserList
+
+  $choosers = GetChooserList();
+
+Return an arrayref containing borrowernumbers and names of all known
+choosers, or undef.
+
+=cut
+
+sub GetChooserList {
+    return GetHouseboundList("CHO");
+}
+
+=head2 GetHouseboundList
+
+  $patrons = GetHouseboundList( $brw_cat_code );
+
+Return an arrayref containing borrowernumbers and names of all
+borrowers with $BRW_CAT_CODE, or undef.
+
+=cut
+
+sub GetHouseboundList {
+    my ( $cat_code ) = @_;
+    my $dbh = C4::Context->dbh;
+    my $sql = "SELECT borrowernumber as volbornumber,
+ concat(title, ' ', firstname, ' ', surname) as fullname
+ from borrowers where categorycode=?
+ order by surname, firstname asc";
+      return $dbh->selectall_arrayref( $sql, { Slice => {} }, $cat_code );
+}
+
+=head2 GetHouseboundInstanceDetails
+
+  $housebound = GetHouseboundInstanceDetails( $instance_id );
+
+Return a hashref containing all details of the housebound_instance
+associated with $INSTANCE_ID or undef.
+
+=cut
+
+sub GetHouseboundInstanceDetails {
+    my ($instanceid) = @_;
+    if ($instanceid) {
+        my $dbh = C4::Context->dbh;
+        return $dbh->selectrow_hashref(
+            'SELECT * from housebound_instance where instanceid=?',
+            {}, $instanceid );
+    }
+    return;
+}
+
+=head2 UpdateHouseboundInstanceDetails
+
+  UpdateHouseboundInstanceDetails( $instanceid, $hbnumber, $dmy,
+                                   $time, $borrowernumber, $volunteer,
+                                   $chooser, $deliverer );
+
+Update housebound_instance table's row matching $instanceid with our
+arguments. The return value is undef.
+
+=cut
+
+sub UpdateHouseboundInstanceDetails {
+    my ( $instanceid, $hbnumber, $dmy, $time, $borrowernumber, $volunteer,
+        $chooser, $deliverer )
+      = @_;
+    if ($hbnumber) {
+        my $dbh = C4::Context->dbh;
+        my $sth = $dbh->prepare(
+'update housebound_instance set hbnumber=?, dmy=?, time=?, borrowernumber=?, volunteer=?, chooser=?, deliverer=? where instanceid=?'
+        );
+        $sth->execute( $hbnumber, $dmy, $time, $borrowernumber, $volunteer,
+            $chooser, $deliverer, $instanceid );
+    }
+    return;
+}
+
+=head2 CreateHouseboundInstanceDetails
+
+  CreateHouseboundInstanceDetails( $hbnumber, $dmy, $time,
+                                   $borrowernumber, $volunteer,
+                                   $chooser, $deliverer );
+
+Create a new housebound_instance table row containing our
+arguments. The return value is undef.
+
+=cut
+
+sub CreateHouseboundInstanceDetails {
+    my ( $hbnumber, $dmy, $time, $borrowernumber, $volunteer, $chooser,
+        $deliverer )
+      = @_;
+    if ($borrowernumber) {
+        my $dbh = C4::Context->dbh;
+        my $sth = $dbh->prepare(
+'insert into housebound_instance (hbnumber, dmy, time, borrowernumber, volunteer, chooser, deliverer) values (?,?,?,?,?,?,?)'
+        );
+        $sth->execute( $hbnumber, $dmy, $time, $borrowernumber, $volunteer,
+            $chooser, $deliverer );
+    }
+    return;
+}
+
+=head2 DeleteHouseboundInstanceDetails
+
+  DeleteHouseboundInstanceDetails( $instanceid );
+
+Delete the row from housebound_instance identified by
+$INSTANCEID. The return value is undef.
+
+=cut
+
+sub DeleteHouseboundInstanceDetails {
+    my ($instanceid) = @_;
+    if ($instanceid) {
+        my $dbh = C4::Context->dbh;
+        my $sth =
+          $dbh->prepare('delete from housebound_instance where instanceid=?');
+        $sth->execute($instanceid);
+    }
+    return;
+}
+
+=head2 CheckPrevIssue
+
+  CheckPrevIssue( $borrowernumber, $biblionumber );
+
+Return 1 if $BIBLIONUMBER has previously been issued to
+$BORROWERNUMBER, 0 otherwise.
+
+=cut
+
+sub CheckPrevIssue {
+    my ( $borrowernumber, $biblionumber ) = @_;
+    my $dbh       = C4::Context->dbh;
+    my $previssue = 0;
+    my $sth =
+      $dbh->prepare('select itemnumber from items where biblionumber=?');
+    $sth->execute($biblionumber);
+
+    my $query = $dbh->prepare(
+'select count(itemnumber) from old_issues where borrowernumber=? and itemnumber=?'
+    );
+    while ( my @row = $sth->fetchrow_array() ) {
+        $query->execute( $borrowernumber, $row[0] );
+        while ( my @matches = $query->fetchrow_array() ) {
+            if ( $matches[0] > 0 ) {
+                $previssue = 1;
+            }
+        }
+    }
+    return $previssue;
+}
+
+1;
+
+__END__
+
+=head1 AUTHOR
+
+Mark Gavillet
+Alex Sassmannshausen
+
+=cut
diff --git a/installer/data/mysql/atomicupdate/housebound_tables.sql b/installer/data/mysql/atomicupdate/housebound_tables.sql
new file mode 100644
index 0000000..ee509f8
--- /dev/null
+++ b/installer/data/mysql/atomicupdate/housebound_tables.sql
@@ -0,0 +1,41 @@
+CREATE TABLE IF NOT EXISTS `housebound` (
+  `hbnumber` int(11) NOT NULL auto_increment,
+  `day` text NOT NULL,
+  `frequency` text,
+  `borrowernumber` int(11) default NULL,
+  `Itype_quant` varchar(10) default NULL,
+  `Item_subject` text,
+  `Item_authors` text,
+  `referral` text,
+  `notes` text,
+  PRIMARY KEY  (`hbnumber`),
+  KEY `borrowernumber` (`borrowernumber`)
+) ENGINE=InnoDB  DEFAULT CHARSET=utf8 ;
+
+
+
+CREATE TABLE IF NOT EXISTS `housebound_instance` (
+  `instanceid` int(11) NOT NULL auto_increment,
+  `hbnumber` int(11) NOT NULL,
+  `dmy` date default NULL,
+  `time` text,
+  `borrowernumber` int(11) NOT NULL,
+  `volunteer` int(11) default NULL,
+  `chooser` int(11) default NULL,
+  `deliverer` int(11) default NULL,
+  PRIMARY KEY  (`instanceid`),
+  KEY `hbnumber` (`hbnumber`,`borrowernumber`,`volunteer`,`chooser`,`deliverer`)
+) ENGINE=InnoDB  DEFAULT CHARSET=utf8 ;
+
+insert into systempreferences (variable,value,options,explanation,type) values
+('useHouseboundModule',1,'','If ON, use the Housebound module','YesNo');
+
+insert into systempreferences (variable,value,options,explanation,type) values
+('useHouseboundCheckPrevious',1,'','If ON, checks if Housebound patrons have previous issued items to be checked out',
+'YesNo');
+
+insert into categories (categorycode, description, enrolmentperiod, upperagelimit, dateofbirthrequired, category_type)
+values ('VOL','Housebound volunteer','99','999','1','A'),('HB','Housebound patron','99','999','1','A'),('DELIV','Housebound deliverer','99','999','1','A'),('CHO','Housebound chooser','99','999','1','A');
+
+insert into authorised_values (category, authorised_value, lib)
+values ('Day','Example_Day','Change Me In Authorised Values (Day)'), ('Frequency','Example_Freq','Change Me In Authorised Values (Frequency)');
diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc
index 6162293..8331639 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc
+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc
@@ -76,6 +76,9 @@
     [% IF ( CAN_user_parameters ) %]
         [% IF ( logview ) %]<li class="active">[% ELSE %]<li>[% END %]<a href="/cgi-bin/koha/tools/viewlog.pl?do_it=1&amp;modules=MEMBERS&amp;modules=circulation&amp;object=[% borrowernumber %]&amp;src=circ">Modification log</a></li>
     [% END %]
+    [% IF useHouseboundModule %]
+        [% IF houseboundview %]<li class="active">[% ELSE %]<li>[% END %]<a href="/cgi-bin/koha/members/housebound.pl?borrowernumber=[% borrowernumber %]">Housebound</a></li>
+    [% END %]
     [% IF ( EnhancedMessagingPreferences ) %]
 	[% IF ( sentnotices ) %]<li class="active">[% ELSE %]<li>[% END %]<a href="/cgi-bin/koha/members/notices.pl?borrowernumber=[% borrowernumber %]">Notices</a></li>
     [% END %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt
index 7c611dd..293aa6b 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt
@@ -273,6 +273,10 @@ var MSG_EXPORT_SELECT_CHECKOUTS = _("You must select checkout(s) to export");
 [% IF  HIGHHOLDS %]
     <li>High demand item. Loan period shortened to [% HIGHHOLDS.duration %] days (due [% HIGHHOLDS.returndate %]). Check out anyway?</li>
 [% END %]
+
+[% IF HOUSEBOUNDPREVISSUE %]
+    <li>Housebound patron has previously issued this item.  Check out anyway?</li>
+[% END %]
 </ul>
 
 [% IF HIGHHOLDS %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/housebound.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/housebound.tt
new file mode 100644
index 0000000..4311668
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/housebound.tt
@@ -0,0 +1,69 @@
+[% USE AuthorisedValues %]
+[% INCLUDE 'doc-head-open.inc' %]
+<title>Koha Housebound - [% surname %], [% firstname %]</title>
+[% INCLUDE 'doc-head-close.inc' %]
+
+</head>
+<body>
+[% INCLUDE 'header.inc' %]
+[% INCLUDE 'patron-search.inc' %]
+
+<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/members/members-home.pl">Patrons</a>  &rsaquo; Housebound details for [% firstname %] [% surname %] [% IF ( cardnumber ) %]([% cardnumber %])[% END %]</div>
+
+<div id="doc3" class="yui-t2">
+    <div id="bd">
+        <div id="yui-main">
+            <div class="yui-b"
+                <h1>Housebound details for [% title %] [% firstname %] [% surname %] [% IF ( cardnumber ) %]([% cardnumber %])[% END %]</h1>
+                [% IF ( housebound.hbnumber ) %]
+                    <div><ul class="toolbar"><li><span class="yui-button yui-link-button"><span class="first-child"><a href="/cgi-bin/koha/members/houseboundedit.pl?hbnumber=[% housebound.hbnumber %]&borrowernumber=[% borrowernumber %]&op=edit">Edit</a></span></span></li></ul></div>
+                    <div id="houseboundinfo" class="rows">
+                        <ol>
+                            <li>
+                                <span class="label">Delivery day:</span>
+                                [% AuthorisedValues.GetByCode( 'day', housebound.day, 0 )
+                                   || housebound.day %]
+                            </li>
+                            <li>
+                                <span class="label">Frequency:</span>
+                                [% AuthorisedValues.GetByCode( 'frequency', housebound.frequency, 0 )
+                                   || housebound.frequency %]
+                            </li>
+                            <li><span class="label">Material:</span>[% housebound.Itype_quant %]</li>
+                            <li><span class="label">Subjects:</span>[% housebound.Item_subject %]</li>
+                            <li><span class="label">Authors:</span>[% housebound.Item_authors %]</li>
+                            <li><span class="label">Referral:</span>[% housebound.referral %]</li>
+                            <li><span class="label">Notes:</span>[% housebound.notes %]</li>
+                        </ol>
+                        <br/>
+                        <div style="height:10px;clear:both;"></div>
+                        <h1>Deliveries</h1>
+                        <div><ul class="toolbar"><li><span class="yui-button yui-link-button"><span class="first-child"><a href="/cgi-bin/koha/members/houseboundinstances.pl?op=add&hbnumber=[% housebound.hbnumber %]&borrowernumber=[% borrowernumber %]">Add a new delivery</a></span></span></li></ul></div>
+                        <table border="0" width="100%" cellpadding="3" cellspacing="0">
+                            <tr>
+                                <th>ID</th><th>Date</th><th>Volunteer</th><th>Chooser</th>
+                                <th>Deliverer</th><th>Actions</th>
+                            </tr>
+                            [% FOREACH instance IN instanceloop %]
+                                <tr>
+                                    <td>[% instance.instanceid %]</td>
+                                    <td>[% instance.dmy %] ([% instance.time %])</td>
+                                    <td><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% instance.volunteer %]">[% instance.vol %]</a></td>
+                                    <td><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% instance.chooser %]">[% instance.cho %]</a></td>
+                                    <td><a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% instance.deliverer %]">[% instance.del %]</a></td>
+                                    <td align="center"><a href="/cgi-bin/koha/members/houseboundinstances.pl?op=edit&instanceid=[% instance.instanceid %]&borrowernumber=[% instance.borrowernumber %]&hbnumber=[% instance.hbnumber %]">Edit</a> | <a href="/cgi-bin/koha/members/houseboundinstances.pl?op=del&instanceid=[% instance.instanceid %]&borrowernumber=[% instance.borrowernumber %]&hbnumber=[% instance.hbnumber %]">Delete</a></td>
+                                </tr>
+                            [% END %]
+                        </table>
+                    </div>
+                [% ELSE %]
+                    <p>This patron does not currently have registered housebound details. To add these details <a href="/cgi-bin/koha/members/houseboundedit.pl?hnbnumber=[% housebound.hbnumber %]&borrowernumber=[% borrowernumber %]&op=add">click here</a>.</p>
+                [% END %]
+            </div>
+        </div>
+        <div class="yui-b">
+            [% INCLUDE 'circ-menu.inc' %]
+        </div>
+    </div>
+</div>
+[% INCLUDE 'intranet-bottom.inc' %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/houseboundedit.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/houseboundedit.tt
new file mode 100644
index 0000000..3668fac
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/houseboundedit.tt
@@ -0,0 +1,102 @@
+[% INCLUDE 'doc-head-open.inc' %]
+<title>Koha Housebound - [% surname %], [% firstname %]</title>
+[% INCLUDE 'doc-head-close.inc' %]
+<script type="text/javascript" language="javascript">
+//<![CDATA[
+    $(document).ready(function() {
+        $("#editform").validate()
+    });
+</script>
+</head>
+<body>
+    [% INCLUDE 'header.inc' %]
+    [% INCLUDE 'patron-search.inc' %]
+
+    <div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/members/members-home.pl">Patrons</a>  &rsaquo; Edit housebound details for [% firstname %] [% surname %] [% IF ( cardnumber ) %]([% cardnumber %])[% END %]</div>
+
+    <div id="doc3" class="yui-t2">
+        <div id="bd">
+            <div id="yui-main">
+                <div class="yui-b">
+                    <h1>Edit housebound details for [% title %] [% firstname %] [% surname %] [% IF ( cardnumber ) %]([% cardnumber %])[% END %]</h1>
+
+                    <form id="editform" method="post" action="/cgi-bin/koha/members/houseboundedit.pl" name="form">
+                        <input type="hidden" name="borrowernumber" value="[% borrowernumber %]" />
+                        <input type="hidden" name="hbnumber" value="[% housebound.hbnumber %]" />
+                        <input type="hidden" name="op" value="[% opeditsubmit %][% opaddsubmit %]" />
+                        <fieldset id="houseboundentry" class="rows">
+                            <legend>Housebound details</legend>
+                            <ol>
+                                <li>
+                                    <label for="day">Delivery day:</label>
+                                    <select id="day" name="day" required>
+                                        <option value="">Select a day</option>
+                                        [% FOREACH d IN days %]
+                                            [% IF ( housebound.day == d.authorised_value ) %]
+                                                <option value="[% d.authorised_value %]" selected="selected">
+                                            [% ELSE %]
+                                                <option value="[% d.authorised_value %]">
+                                            [% END %]
+                                                [% IF ( d.lib ) %]
+                                                    [% d.lib %]
+                                                [% ELSE %]
+                                                    [% d.authorised_value %]
+                                                [% END %]
+                                             </option>
+                                        [% END %]
+                                    </select>
+                                </li>
+                                <li>
+                                    <label for="frequency">Frequency:</label>
+                                    <select id="frequency" name="frequency" required>
+                                        <option value="">Select a frequency</option>
+                                        [% FOREACH f IN frequencies %]
+                                            [% IF ( housebound.frequency == f.authorised_value ) %]
+                                                <option value="[% f.authorised_value %]" selected="selected">
+                                            [% ELSE %]
+                                                <option value="[% f.authorised_value %]">
+                                            [% END %]
+                                                [% IF ( f.lib ) %]
+                                                    [% f.lib %]
+                                                [% ELSE %]
+                                                    [% f.authorised_value %]
+                                                [% END %]
+                                             </option>
+                                        [% END %]
+                                    </select>
+                                </li>
+                                <li>
+                                    <label for="Itype_quant">Material:</label>
+                                    <input id="Itype_quant" type="text" value="[% housebound.Itype_quant %]" size="50" name="Itype_quant">
+                                </li>
+                                <li>
+                                    <label for="Item_subject">Subjects:</label>
+                                    <input id="Item_subject" type="text" value="[% housebound.Item_subject %]" size="50" name="Item_subject">
+                                </li>
+                                <li>
+                                    <label for="Item_authors">Authors:</label>
+                                    <input id="Item_authors" type="text" value="[% housebound.Item_authors %]" size="50" name="Item_authors">
+                                </li>
+                                <li>
+                                    <label for="referral">Referral:</label>
+                                    <input id="referral" type="text" value="[% housebound.referral %]" size="50" name="referral">
+                                </li>
+                                <li>
+                                    <label for="notes">Notes:</label>
+                                    <input id="notes" type="text" value="[% housebound.notes %]" size="50" name="notes">
+                                </li>
+                            </ol>
+                        </fieldset>
+                        <fieldset class="action">
+                            <input type="submit" value="Save changes" onclick="return check_form_housebound();" name="save">
+                            <a class="cancel" href="/cgi-bin/koha/members/housebound.pl?borrowernumber=[% borrowernumber %]">Cancel</a>
+                        </fieldset>
+                    </form>
+                </div>
+            </div>
+            <div class="yui-b">
+                [% INCLUDE 'circ-menu.inc' %]
+            </div>
+        </div>
+    </div>
+    [% INCLUDE 'intranet-bottom.inc' %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/houseboundinstances.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/houseboundinstances.tt
new file mode 100644
index 0000000..460ef5c
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/houseboundinstances.tt
@@ -0,0 +1,133 @@
+[% USE KohaDates %]
+[% INCLUDE 'doc-head-open.inc' %]
+<title>Koha Housebound - [% surname %], [% firstname %]</title>
+[% INCLUDE 'doc-head-close.inc' %]
+[% INCLUDE 'calendar.inc' %]
+<script type="text/javascript" language="javascript">
+//<![CDATA[
+    $(document).ready(function() {
+        $("#dmy").datepicker({ minDate: "-1D",
+                               dateFormat: "yy-mm-dd"
+                             })
+        $("#instance_form").validate()
+    });
+    function deleteInstance()
+    {
+        window.location="/cgi-bin/koha/members/houseboundinstances.pl?op=delsubmit&borrowernumber=[% borrowernumber %]&instanceid=[% delinstanceid %]&hbnumber=[% housebound.hbnumber %]";
+    }
+    function cancelInstance()
+    {
+        window.location="/cgi-bin/koha/members/housebound.pl?borrowernumber=[% borrowernumber %]";
+    }
+</script>
+</head>
+<body>
+    [% INCLUDE 'header.inc' %]
+    [% INCLUDE 'patron-search.inc' %]
+
+    <div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/members/members-home.pl">Patrons</a>  &rsaquo; Housebound deliveries for [% firstname %] [% surname %] [% IF ( cardnumber ) %]([% cardnumber %])[% END %]</div>
+    <div id="doc3" class="yui-t2">
+        <div id="bd">
+            <div id="yui-main">
+                <div class="yui-b">
+                    <h1>Housebound deliveries for [% title %] [% firstname %] [% surname %] [% IF ( cardnumber ) %]([% cardnumber %])[% END %]</h1>
+                    <form name="form" id="instance_form" method="post" action="/cgi-bin/koha/members/houseboundinstances.pl">
+                        <input type="hidden" name="op" value="[% op %]" />
+                        <input type="hidden" name="borrowernumber" value="[% borrowernumber %]" />
+                        <input type="hidden" name="hbnumber" value="[% housebound.hbnumber %]" />
+                        <input type="hidden" name="instanceid" value="[% instanceid %]" />
+                        [% IF ( opdel ) %]
+                            <p>Are you sure you want to delete this delivery?</p>
+                            <p><button id="ybutton" onclick="deleteInstance(); return false;">YES</button>&nbsp;&nbsp;<button id="nbutton" onclick="cancelInstance(); return false;">NO</button></p>
+                        [% ELSE %]
+                            <fieldset class="rows" id="instance">
+                                <legend>Delivery details</legend>
+                                <ol>
+                                    <li>
+                                        <label for="dmy">Date: </label>
+                                        <input type="text" id="dmy" name="dmy" size="20" value="[% instance.dmy %]" required/>
+                                    </li>
+                                    <li>
+                                        <label for="time">Time:</label>
+                                        <select id="time" name="time" required>
+                                            <option value="">Select a time</option>
+                                            [% IF ( instance.time == 'am' ) %]
+                                                <option value="am" selected="selected">
+                                            [% ELSE %]
+                                                <option value="am">
+                                            [% END %]
+                                                am
+                                            </option>
+                                            [% IF ( instance.time == 'pm' ) %]
+                                                <option value="pm" selected="selected">
+                                            [% ELSE %]
+                                                <option value="pm">
+                                            [% END %]
+                                                pm
+                                            </option>
+                                        </select>
+                                    </li>
+                                    <li>
+                                        <label for="volunteer">Chooser/Deliverer:</label>
+                                        <select id="volunteer" name="volunteer">
+                                            <option value="">Select a chooser_deliverer</option>
+                                            [% FOREACH vol IN volunteerlist %]
+                                                    [% IF ( vol.volbornumber == instance.volunteer ) %]
+                                                        <option value="[% vol.volbornumber %]"
+                                                                selected="selected">
+                                                    [% ELSE %]
+                                                        <option value="[% vol.volbornumber %]">
+                                                    [% END %]
+                                                        [% vol.fullname %]
+                                                    </option>
+                                            [% END %]
+                                        </select>
+                                    </li>
+                                    <li>
+                                        <label for="chooser">Chooser:</label>
+                                        <select id="chooser" name="chooser">
+                                            <option value="">Select a chooser</option>
+                                            [% FOREACH cho IN chooserlist %]
+                                                [% IF ( cho.volbornumber == instance.chooser ) %]
+                                                    <option value="[% cho.volbornumber %]"
+                                                            selected="selected">
+                                                [% ELSE %]
+                                                    <option value="[% cho.volbornumber %]">
+                                                [% END %]
+                                                    [% cho.fullname %]
+                                                </option>
+                                            [% END %]
+                                        </select>
+                                    </li>
+                                    <li>
+                                        <label for="deliverer">Deliverer:</label>
+                                        <select id="deliverer" name="deliverer">
+                                            <option value="">Select a deliverer</option>
+                                            [% FOREACH del IN delivererlist %]
+                                                [% IF ( del.volbornumber == instance.deliverer ) %]
+                                                    <option value="[% del.volbornumber %]"
+                                                            selected="selected">
+                                                [% ELSE %]
+                                                    <option value="[% del.volbornumber %]">
+                                                [% END %]
+                                                    [% del.fullname %]
+                                                </option>
+                                            [% END %]
+                                        </select>
+                                    </li>
+                                </ol>
+                            </fieldset>
+                            <fieldset class="action">
+                                <input type="submit" value="Save" name="save">
+                                <a class="cancel" href="/cgi-bin/koha/members/housebound.pl?borrowernumber=[% borrowernumber %]">Cancel</a>
+                            </fieldset>
+                        [% END %]
+                    </form>
+                </div>
+            </div>
+            <div class="yui-b">
+                [% INCLUDE 'circ-menu.inc' %]
+            </div>
+        </div>
+    </div>
+    [% INCLUDE 'intranet-bottom.inc' %]
diff --git a/members/housebound.pl b/members/housebound.pl
new file mode 100755
index 0000000..2b1bccb
--- /dev/null
+++ b/members/housebound.pl
@@ -0,0 +1,91 @@
+#!/usr/bin/perl
+
+# Copyright 2011 Mark Gavillet
+# Copyright 2014 PTFS-Europe Ltd
+#
+# 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 3 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.
+
+use strict;
+use warnings;
+use CGI;
+use C4::Context;
+use C4::Auth;
+use C4::Output;
+use C4::Members;
+use C4::Koha;
+use C4::Branch;
+use C4::Housebound qw( GetHouseboundDetails
+                       GetCurrentHouseboundInstanceList );
+
+my $input = CGI->new();
+
+sub nl2br {
+    my ($t) = shift or return;
+    $t =~ s/\n/\n<br>/g;
+    return ($t);
+}
+
+my $dbh = C4::Context->dbh;
+
+my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+    {
+        template_name   => 'members/housebound.tmpl',
+        query           => $input,
+        type            => 'intranet',
+        authnotrequired => 0,
+        flagsrequired   => { borrowers => 1 },
+        debug           => $ENV{DEBUG},
+    }
+);
+
+my $borrowernumber = $input->param('borrowernumber');
+
+my $borrowerdetails      = C4::Members::GetMemberDetails($borrowernumber);
+my $branchdetail         = GetBranchDetail( $borrowerdetails->{branchcode} );
+my $categorydetail       = GetMember( borrowernumber => $borrowernumber);
+my $housebound           = GetHouseboundDetails($borrowernumber);
+my $housebound_instances = GetCurrentHouseboundInstanceList($borrowernumber);
+
+$template->param(
+    surname        => $borrowerdetails->{surname},
+    firstname      => $borrowerdetails->{firstname},
+    cardnumber     => $borrowerdetails->{cardnumber},
+    borrowernumber => $borrowerdetails->{borrowernumber},
+    houseboundview => 'on',
+    address        => $borrowerdetails->{address},
+    address2       => $borrowerdetails->{address2},
+    city           => $borrowerdetails->{city},
+    phone          => $borrowerdetails->{phone},
+    phonepro       => $borrowerdetails->{phonepro},
+    mobile         => $borrowerdetails->{mobile},
+    email          => $borrowerdetails->{email},
+    emailpro       => $borrowerdetails->{emailpro},
+    categoryname   => $categorydetail->{description},
+    categorycode   => $borrowerdetails->{categorycode},
+    branch         => $borrowerdetails->{branch},
+    branchname     => $branchdetail->{branchname},
+    zipcode        => $borrowerdetails->{zipcode}
+);
+
+$template->param(
+    housebound   => $housebound
+);
+
+if ($housebound_instances) {
+    $template->param( instanceloop => $housebound_instances );
+}
+
+output_html_with_http_headers $input, $cookie, $template->output;
diff --git a/members/houseboundedit.pl b/members/houseboundedit.pl
new file mode 100755
index 0000000..ced3cfb
--- /dev/null
+++ b/members/houseboundedit.pl
@@ -0,0 +1,114 @@
+#!/usr/bin/perl
+
+# Copyright 2011 Mark Gavillet
+# Copyright 2014 PTFS-Europe Ltd
+#
+# 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 3 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.
+
+use strict;
+use warnings;
+use CGI;
+use C4::Context;
+use C4::Auth;
+use C4::Output;
+use C4::Members;
+use C4::Koha;
+use C4::Branch;
+use C4::Housebound qw( GetHouseboundDetails
+                       UpdateHouseboundDetails
+                       CreateHouseboundDetails );
+
+my $input = CGI->new();
+
+my $dbh = C4::Context->dbh;
+
+my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+    {
+        template_name   => 'members/houseboundedit.tmpl',
+        query           => $input,
+        type            => 'intranet',
+        authnotrequired => 0,
+        flagsrequired   => { borrowers => 1 },
+        debug           => $ENV{DEBUG},
+    }
+);
+
+my $borrowernumber = $input->param('borrowernumber');
+my $op             = $input->param('op');
+if ( $op eq 'editsubmit' ) {
+    UpdateHouseboundDetails(
+        $input->param('hbnumber'),     $input->param('borrowernumber'),
+        $input->param('day'),          $input->param('frequency'),
+        $input->param('Itype_quant'),  $input->param('Item_subject'),
+        $input->param('Item_authors'), $input->param('referral'),
+        $input->param('notes')
+    );
+    my $url =
+      "/cgi-bin/koha/members/housebound.pl?borrowernumber=$borrowernumber";
+    print "Location: $url";
+}
+if ( $op eq 'addsubmit' ) {
+    CreateHouseboundDetails(
+        $input->param('borrowernumber'), $input->param('day'),
+        $input->param('frequency'),      $input->param('Itype_quant'),
+        $input->param('Item_subject'),   $input->param('Item_authors'),
+        $input->param('referral'),       $input->param('notes')
+    );
+    my $url =
+      "/cgi-bin/koha/members/housebound.pl?borrowernumber=$borrowernumber";
+    print "Location: $url";
+}
+if ( $op eq 'edit' ) {
+    $template->param( opeditsubmit => 'editsubmit' );
+}
+if ( $op eq 'add' ) {
+    $template->param( opaddsubmit => 'addsubmit' );
+}
+
+my $borrowerdetails = C4::Members::GetMemberDetails($borrowernumber);
+my $branchdetail    = GetBranchDetail( $borrowerdetails->{branchcode} );
+my $categorydetail  = GetMember( borrowernumber => $borrowernumber );
+my $housebound      = GetHouseboundDetails($borrowernumber);
+
+
+$template->param(
+    surname        => $borrowerdetails->{surname},
+    firstname      => $borrowerdetails->{firstname},
+    cardnumber     => $borrowerdetails->{cardnumber},
+    borrowernumber => $borrowerdetails->{borrowernumber},
+    address        => $borrowerdetails->{address},
+    address2       => $borrowerdetails->{address2},
+    city           => $borrowerdetails->{city},
+    phone          => $borrowerdetails->{phone},
+    phonepro       => $borrowerdetails->{phonepro},
+    mobile         => $borrowerdetails->{mobile},
+    email          => $borrowerdetails->{email},
+    emailpro       => $borrowerdetails->{emailpro},
+    categoryname   => $categorydetail->{description},
+    categorycode   => $borrowerdetails->{categorycode},
+    branch         => $borrowerdetails->{branch},
+    branchname     => $branchdetail->{branchname},
+    zipcode        => $borrowerdetails->{zipcode}
+);
+
+$template->param(
+    housebound   => $housebound,
+    houseboundview => 'on',
+    days         => GetAuthorisedValues('day'),
+    frequencies  => GetAuthorisedValues('frequency'),
+);
+
+output_html_with_http_headers $input, $cookie, $template->output;
diff --git a/members/houseboundinstances.pl b/members/houseboundinstances.pl
new file mode 100755
index 0000000..ec64e8f
--- /dev/null
+++ b/members/houseboundinstances.pl
@@ -0,0 +1,138 @@
+#!/usr/bin/perl
+
+# Copyright 2011 Mark Gavillet
+# Copyright 2014 PTFS-Europe Ltd
+#
+# 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 3 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.
+
+use strict;
+use warnings;
+use CGI;
+use C4::Context;
+use C4::Auth;
+use C4::Output;
+use C4::Members;
+use C4::Koha;
+use C4::Branch;
+use C4::Dates;
+use C4::Housebound qw( GetHouseboundDetails
+                       GetHouseboundInstanceDetails
+                       CreateHouseboundInstanceDetails
+                       DeleteHouseboundInstanceDetails
+                       UpdateHouseboundInstanceDetails
+                       GetVolunteerList GetChooserList
+                       GetDelivererList );
+
+my $input = CGI->new();
+
+my $dbh = C4::Context->dbh;
+
+my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+    {
+        template_name   => 'members/houseboundinstances.tmpl',
+        query           => $input,
+        type            => 'intranet',
+        authnotrequired => 0,
+        flagsrequired   => { borrowers => 1 },
+        debug           => $ENV{DEBUG},
+    }
+);
+
+my $borrowernumber = $input->param('borrowernumber');
+
+my $op = $input->param('op');
+if ( $op eq 'add' ) {
+    $template->param( opadd => 1, op => 'addsubmit' );
+}
+if ( $op eq 'edit' ) {
+    $template->param( opedit => 1, op => 'editsubmit' );
+}
+if ( $op eq 'del' ) {
+    $template->param( opdel => 1, op => 'delsubmit' );
+}
+if ( $op eq 'addsubmit' ) {
+    CreateHouseboundInstanceDetails(
+        $input->param('hbnumber'),  $input->param('dmy'),
+        $input->param('time'),      $input->param('borrowernumber'),
+        $input->param('volunteer'), $input->param('chooser'),
+        $input->param('deliverer')
+    );
+    my $url =
+      "/cgi-bin/koha/members/housebound.pl?borrowernumber=$borrowernumber";
+    print "Location: $url";
+}
+if ( $op eq 'delsubmit' ) {
+    DeleteHouseboundInstanceDetails( $input->param('instanceid') );
+    my $url =
+      "/cgi-bin/koha/members/housebound.pl?borrowernumber=$borrowernumber";
+    print "Location: $url";
+}
+if ( $op eq 'editsubmit' ) {
+    UpdateHouseboundInstanceDetails(
+        $input->param('instanceid'),     $input->param('hbnumber'),
+        $input->param('dmy'),            $input->param('time'),
+        $input->param('borrowernumber'), $input->param('volunteer'),
+        $input->param('chooser'),        $input->param('deliverer')
+    );
+    my $url =
+      "/cgi-bin/koha/members/housebound.pl?borrowernumber=$borrowernumber";
+    print "Location: $url";
+}
+
+my $borrowerdetails   = C4::Members::GetMemberDetails($borrowernumber);
+my $branchdetail      = GetBranchDetail( $borrowerdetails->{branchcode} );
+my $categorydetail    = GetMember( $borrowernumber, 'borrowernumber' );
+my $housebound        = GetHouseboundDetails($borrowernumber);
+my $volunteerlist     = GetVolunteerList();
+my $chooserlist       = GetChooserList();
+my $delivererlist     = GetDelivererList();
+my $delinstanceid     = $input->param('instanceid');
+my $instanceid        = $input->param('instanceid');
+my $instancedetails   = GetHouseboundInstanceDetails($instanceid);
+
+$template->param(
+    surname        => $borrowerdetails->{surname},
+    firstname      => $borrowerdetails->{firstname},
+    cardnumber     => $borrowerdetails->{cardnumber},
+    borrowernumber => $borrowerdetails->{borrowernumber},
+    address        => $borrowerdetails->{address},
+    address2       => $borrowerdetails->{address2},
+    city           => $borrowerdetails->{city},
+    phone          => $borrowerdetails->{phone},
+    phonepro       => $borrowerdetails->{phonepro},
+    mobile         => $borrowerdetails->{mobile},
+    email          => $borrowerdetails->{email},
+    emailpro       => $borrowerdetails->{emailpro},
+    categoryname   => $categorydetail->{description},
+    categorycode   => $borrowerdetails->{categorycode},
+    branch         => $borrowerdetails->{branch},
+    branchname     => $branchdetail->{branchname},
+    zipcode        => $borrowerdetails->{zipcode}
+);
+
+$template->param(
+    houseboundview           => 'on',
+    housebound               => $housebound,
+    DHTMLcalendar_dateformat => C4::Dates->DHTMLcalendar(),
+    volunteerlist            => $volunteerlist,
+    chooserlist              => $chooserlist,
+    delivererlist            => $delivererlist,
+    delinstanceid            => $delinstanceid,
+    instanceid               => $instanceid,
+    instance                 => $instancedetails,
+);
+
+output_html_with_http_headers $input, $cookie, $template->output;
diff --git a/t/Housebound.t b/t/Housebound.t
new file mode 100755
index 0000000..afbd98e
--- /dev/null
+++ b/t/Housebound.t
@@ -0,0 +1,292 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+use C4::Context;
+use Test::More tests => 29;
+use Test::MockModule;
+use DBD::Mock;
+
+use_ok('C4::Housebound');
+
+use C4::Housebound qw( GetHouseboundDetails CreateHouseboundDetails
+                       UpdateHouseboundDetails
+                       GetCurrentHouseboundInstanceList
+                       GetHouseboundInstanceDetails
+                       UpdateHouseboundInstanceDetails
+                       CreateHouseboundInstanceDetails
+                       DeleteHouseboundInstanceDetails
+                       GetVolunteerNameAndID GetVolunteerList
+                       GetChooserList GetDelivererList CheckPrevIssue
+                       );
+
+# Setup mock db
+my $module_context = new Test::MockModule('C4::Context');
+$module_context->mock(
+    '_new_dbh',
+    sub {
+        my $dbh = DBI->connect( 'DBI:Mock:', '', '' )
+          || die "Cannot create handle: $DBI::errstr\n";
+        return $dbh;
+    }
+);
+
+my $dbh = C4::Context->dbh();
+
+# convenience variables
+my $name;
+# mock_add_resultset vars
+my ( $sql, $sql2, @bound_params, @keys, @values, %result ) = ( );
+# mock_history_verification vars
+my ( $history, $params, $query ) = ( );
+
+sub clean_vars {
+    ( $name, $sql, $sql2, @bound_params, @keys, @values, %result,
+      $history, $params, $query ) = ( );
+    $dbh->{mock_clear_history} = 1;
+}
+
+# Tests
+## GetHouseboundDetails
+$name         = 'GetHouseboundDetails';
+$sql          = 'select * from housebound where borrowernumber=?';
+@bound_params = ( 101 );
+@keys         = qw< hbnumber day frequency borrowernumber Itype_quant
+                    Item_subject Item_authors referral notes >;
+@values       = qw< 5 Monday Week-2/4 101 books dvd thrillers cooke
+                    alex none >;
+foreach my $i ( 0 .. $#keys) {
+    $result{$keys[$i]} = $values[$i];
+}
+
+is( GetHouseboundDetails('unknown'), undef,
+    $name . ': Return value "undefined".' );
+
+$dbh->{mock_add_resultset} =
+  {
+   sql          => $sql,
+   bound_params => [ @bound_params ],
+   results      => [ [ @keys ], [ @values ] ],
+  };
+
+is_deeply( GetHouseboundDetails($bound_params[0]), \%result,
+           $name . ': Return value "$result".' );
+
+clean_vars();
+
+## CreateHouseboundDetails
+$name      = 'CreateHouseboundDetails';
+$sql       = 'insert into housebound'
+  . ' (day, frequency, borrowernumber, Itype_quant, Item_subject,'
+  . ' Item_authors, referral, notes) values (?,?,?,?,?,?,?,?)';
+@keys      = qw< day frequency borrowernumber Itype_quant Item_subject
+                 Item_authors referral notes >;
+@values    = qw< Tuesday Week-3/4 103 journals psychology sam bob $name >;
+
+is( CreateHouseboundDetails($values[2], $values[0], $values[1], $values[3],
+                            $values[4], $values[5], $values[6], $values[7]),
+    undef, $name . ': Return value "undefined".');
+
+$history = $dbh->{mock_all_history};
+$query = $history->[0];
+$params = $query->bound_params;
+
+is($query->statement, $sql, $name . ': Correct statement generated' );
+is(scalar(@{$params}), scalar(@values),
+   $name . ': Correct number of parameters bound.' );
+is_deeply( $params, \@values,
+           $name . ': Correct parameter values passed.' );
+
+clean_vars();
+
+## UpdateHouseboundDetails
+$name = 'UpdateHouseboundDetails';
+$sql  = 'update housebound set day=?,frequency=?,borrowernumber=?,'
+  . 'Itype_quant=?,Item_subject=?,Item_authors=?,referral=?,notes=?'
+  . ' where hbnumber=?',
+@keys      = qw< day frequency borrowernumber Itype_quant Item_subject
+                 Item_authors referral notes hbnumber>;
+@values    = qw< Tuesday Week-3/4 103 journals psychology sam bob $name 1>;
+is( UpdateHouseboundDetails( $values[8], $values[2], $values[0], $values[1],
+                             $values[3], $values[4], $values[5], $values[6],
+                             $values[7] ),
+    undef, $name . ': Return value "undefined".');
+
+$history = $dbh->{mock_all_history};
+$query = $history->[0];
+$params = $query->bound_params;
+
+is($query->statement, $sql, $name . ': Correct statement generated' );
+is(scalar(@{$params}), scalar(@values),
+   $name . ': Correct number of parameters bound.');
+is_deeply( $params, \@values,
+           $name . ': Correct parameter values passed.');
+
+clean_vars();
+
+## GetCurrentHouseboundInstanceList
+### GetcurrentHouseboundInstanceList's Slice causes
+### mock dbd to fail
+### Cannot test.
+
+## GetVolunteerNameAndID
+$name         = "GetVolunteerNameAndID";
+$sql          = 'select borrowernumber,title,firstname,surname'
+  . ' from borrowers where borrowernumber=?';
+@bound_params = ( 101 );
+@keys      = qw< borrowernumber title firstname surname >;
+@values    = qw< 101 Mr John Doe >;
+foreach my $i ( 0 .. $#keys) {
+    $result{$keys[$i]} = $values[$i];
+}
+
+is( GetVolunteerNameAndID($bound_params[0]), undef,
+    $name . ': Return value "undefined".' );
+
+$dbh->{mock_add_resultset} =
+  {
+   sql          => $sql,
+   bound_params => [ @bound_params ],
+   results      => [ [ @keys ], [ @values ] ],
+  };
+
+is_deeply( GetVolunteerNameAndID($bound_params[0]),
+           \%result, $name . ': Return value "$result".' );
+
+clean_vars();
+
+## GetVolunteerList
+### GetVolunteerList's Slice causes mock dbd to fail
+### Cannot test.
+
+## GetHouseboundInstanceDetails
+$name         = 'GetHouseboundInstanceDetails';
+$sql          = 'SELECT * from housebound_instance where instanceid=?';
+@bound_params = ( 1 );
+@keys      = qw< instanceid hbnumber dmy time borrowernumber volunteer
+                 chooser deliverer >;
+@values    = qw< 1 5 2014-03-31 am 101 102 103 104 >;
+foreach my $i ( 0 .. $#keys) {
+    $result{$keys[$i]} = $values[$i];
+}
+
+is( GetHouseboundInstanceDetails('1'), undef,
+    $name . ': Return value "undefined".' );
+
+$dbh->{mock_add_resultset} =
+  {
+   sql          => $sql,
+   bound_params => [ @bound_params ],
+   results      => [ [ @keys ], [ @values ] ],
+  };
+
+is_deeply( GetHouseboundInstanceDetails('1'), \%result,
+           $name . ': Return value "$result".' );
+
+clean_vars();
+
+## UpdateHouseboundInstanceDetails
+$name      = "UpdateHouseboundInstanceDetails";
+$sql       = 'update housebound_instance set hbnumber=?, dmy=?,'
+  . ' time=?, borrowernumber=?, volunteer=?, chooser=?, deliverer=?'
+  . ' where instanceid=?';
+@keys      = qw< hbnumber dmy time borrowernumber volunteer chooser
+                 deliverer instanceid >;
+@values    = qw< 5 2014-03-31 am 101 102 103 104 1 >;
+
+is( UpdateHouseboundInstanceDetails( $values[7], $values[0], $values[1],
+                                     $values[2], $values[3], $values[4],
+                                     $values[5], $values[6] ), undef,
+    $name . ': Return value "undefined".' );
+
+$history = $dbh->{mock_all_history};
+$query = $history->[0];
+$params = $query->bound_params;
+
+is($query->statement, $sql, $name . ': Correct statement generated' );
+is(scalar(@{$params}), scalar(@values),
+   $name . ': Correct number of parameters bound.');
+is_deeply( $params, \@values,
+           $name . ': Correct parameter values passed.');
+
+clean_vars();
+
+## CreateHouseboundInstanceDetails
+$name      = "CreateHouseboundInstanceDetails";
+$sql       = 'insert into housebound_instance'
+  . ' (hbnumber, dmy, time, borrowernumber, volunteer, chooser, deliverer)'
+  . ' values (?,?,?,?,?,?,?)';
+@keys      = qw< hbnumber dmy time borrowernumber volunteer chooser
+                 deliverer >;
+@values    = qw< 5 2014-03-31 am 101 102 103 104 >;
+
+is( CreateHouseboundInstanceDetails( @values ), undef,
+    $name . ': Return value "undefined".');
+
+$history = $dbh->{mock_all_history};
+$query = $history->[0];
+$params = $query->bound_params;
+
+is($query->statement, $sql, $name . ': Correct statement generated' );
+is(scalar(@{$params}), scalar(@values),
+   $name . ': Correct number of parameters bound.' );
+is_deeply( $params, \@values,
+           $name . ': Correct parameter values passed.' );
+
+$dbh->{mock_clear_history} = 1;
+
+## DeleteHouseboundInstanceDetails
+$name      = 'DeleteHouseboundInstanceDetails';
+$sql       = 'delete from housebound_instance where instanceid=?';
+@keys      = qw< instanceid >;
+@values    = qw< 1 >;
+
+is( DeleteHouseboundInstanceDetails( @values ), undef,
+    $name . ': Return value "undefined".');
+
+$history = $dbh->{mock_all_history};
+$query = $history->[0];
+$params = $query->bound_params;
+
+is($query->statement, $sql, $name . ': Correct statement generated' );
+is(scalar(@{$params}), scalar(@values),
+   $name . ': Correct number of parameters bound.' );
+is_deeply( $params, \@values,
+           $name . ': Correct parameter values passed.' );
+
+clean_vars();
+
+## CheckPrevIssue
+$name      = 'CheckPrevIssue';
+$sql       = 'select itemnumber from items where biblionumber=?';
+$sql2      = 'select count(itemnumber) from old_issues'
+  . ' where borrowernumber=? and itemnumber=?';
+@keys      = qw< borrowernumber biblionumber itemnumber >;
+@values    = qw< 101 3576 5043 >;
+
+# 1) Prepop items with itemnumber for result
+$dbh->{mock_add_resultset} = {
+    sql          => $sql,
+    bound_params => $keys[1],
+    results      => [ [ ( $keys[2] ) ], [ ( $values[2] ) ] ],
+   };
+# 2) Test if never issued before (expect 0)
+is( CheckPrevIssue( $keys[0], $keys[1] ), 0,
+    $name . ': Return value "no matches".' );
+# 3) Prepop old_issues with itemnumber and borrowernumber
+$dbh->{mock_add_resultset} = {
+    sql          => $sql2,
+    bound_params => [ $keys[0], $keys[2] ],
+    results      => [
+                     [ ( $keys[0], $keys[2] ) ],
+                     [ ( $values[0], $values[2] ) ],
+                     [ ( $values[0], $values[2] ) ],
+                     [ ( $values[0], $values[2] ) ],
+                     [ ( $values[0], $values[2] ) ],
+                    ],
+   };
+# 4) Test if issued before (e.g. 7 times — expect 1)
+is( CheckPrevIssue( $keys[0], $keys[1] ), 1,
+    $name . ': Return value "> 0 matches".' );
+
+clean_vars();
-- 
1.7.10.4