From 5c05dd0a380774d0c11a6156d8d27ca2f93b511f Mon Sep 17 00:00:00 2001
From: Kyle M Hall <kyle@bywatersolutions.com>
Date: Thu, 9 May 2013 10:55:55 -0400
Subject: [PATCH] Bug 10276 - Extend IndependentBranches to support groups of libraries

This patch adds the ability to have independent library groups. To
enable this feature, enable the IndependentBranches system preference.
If no libraries are in any independent library groups,
IndependentBranches will behave as it always have. If a library is
part of an independent library group with one or more libraries, that
library will be able to access and modify patrons and items from any
library in that group.

Let's say we have 3 groups:
Group1:
  LibA
  LibB
  LibC
Group1:
  LibD
  LibE
  LibF
Group3:
  LibG
  LibH
  LibA

Note how LibA is in two groups ( Group1 and Group3 ). With this
configuration, if IndependentBranches is enabled, libraries will be able
to access and edit patrons and items in the following configuration:
LibA => LibB, LibC, LibG, LibH
LibB => LibA, LibC
LibC => LibA, LibB
LibD => LibE, LibF
LibF => LibD, LibE
LibG => LibH, LibA
LibH => LibG, LibA

Furthermore, let us assume there is a library LibI, which does not
belong to any group. That library will only be able to view and edit
patrons and items from it's own library.

Imagine a library consortium consisting of multiple library systems.
This feature would allow a consortium to group libraries by system such
that those systems could work independently from one another on a single
installation.

Test Plan:
1) Apply this patch
2) Run updatedatabase.pl
3) Enable IndependentBranches
4) Test independent branches, no changes should be noted
5) Navigate to admin/branches.pl
6) Create a new Independent library group
7) Add your library and some other libraries to that group
8) Re-test IndependentBranches, everything should work as previously,
   but instead of being limited to just your library, you should have
   access to everything within your library group.
   Example: Try to edit an item. If the item is owned by a library in
            your group, you should be able to edit it.

Signed-off-by:  Joel Sasse <jsasse@plumcreeklibrary.net>
Signed-off-by: Mark Tompsett <mtompset@hotmail.com>
---
 C4/Acquisition.pm                                  |   87 ++++++++-
 C4/Branch.pm                                       |  108 ++++++++++-
 C4/Circulation.pm                                  |   21 ++-
 C4/Items.pm                                        |   31 ++-
 C4/Letters.pm                                      |    1 +
 C4/Members.pm                                      |  112 ++++++-----
 C4/Serials.pm                                      |  109 +++++++++--
 C4/Suggestions.pm                                  |   51 +++--
 acqui/basket.pl                                    |   22 ++-
 admin/branches.pl                                  |   52 ++----
 catalogue/moredetail.pl                            |   15 +-
 cataloguing/additem.pl                             |   13 +-
 circ/circulation-home.pl                           |    7 +-
 circ/pendingreserves.pl                            |    5 +-
 circ/reserveratios.pl                              |    6 +-
 installer/data/mysql/kohastructure.sql             |    2 +-
 installer/data/mysql/updatedatabase.pl             |   18 ++-
 .../prog/en/modules/admin/branches.tt              |  208 +++++++++++++-------
 members/deletemem.pl                               |   14 +-
 t/db_dependent/Branch.t                            |   76 +++++++-
 20 files changed, 715 insertions(+), 243 deletions(-)

diff --git a/C4/Acquisition.pm b/C4/Acquisition.pm
index 15f69c0..cefd9b8 100644
--- a/C4/Acquisition.pm
+++ b/C4/Acquisition.pm
@@ -33,6 +33,7 @@ use Koha::DateUtils qw( dt_from_string output_pref );
 use Koha::Acquisition::Order;
 use Koha::Acquisition::Bookseller;
 use Koha::Number::Price;
+use C4::Branch qw(GetIndependentGroupModificationRights);
 
 use Time::localtime;
 use HTML::Entities;
@@ -1893,6 +1894,77 @@ sub TransferOrder {
 
 =head2 FUNCTIONS ABOUT PARCELS
 
+=cut
+
+#------------------------------------------------------------#
+
+=head3 GetParcel
+
+  @results = &GetParcel($booksellerid, $code, $date);
+
+Looks up all of the received items from the supplier with the given
+bookseller ID at the given date, for the given code (bookseller Invoice number). Ignores cancelled and completed orders.
+
+C<@results> is an array of references-to-hash. The keys of each element are fields from
+the aqorders, biblio, and biblioitems tables of the Koha database.
+
+C<@results> is sorted alphabetically by book title.
+
+=cut
+
+sub GetParcel {
+    #gets all orders from a certain supplier, orders them alphabetically
+    my ( $supplierid, $code, $datereceived ) = @_;
+    my $dbh     = C4::Context->dbh;
+    my @results = ();
+    $code .= '%'
+    if $code;  # add % if we search on a given code (otherwise, let him empty)
+    my $strsth ="
+        SELECT  authorisedby,
+                creationdate,
+                aqbasket.basketno,
+                closedate,surname,
+                firstname,
+                aqorders.biblionumber,
+                aqorders.ordernumber,
+                aqorders.parent_ordernumber,
+                aqorders.quantity,
+                aqorders.quantityreceived,
+                aqorders.unitprice,
+                aqorders.listprice,
+                aqorders.rrp,
+                aqorders.ecost,
+                aqorders.gstrate,
+                biblio.title
+        FROM aqorders
+        LEFT JOIN aqbasket ON aqbasket.basketno=aqorders.basketno
+        LEFT JOIN borrowers ON aqbasket.authorisedby=borrowers.borrowernumber
+        LEFT JOIN biblio ON aqorders.biblionumber=biblio.biblionumber
+        LEFT JOIN aqinvoices ON aqorders.invoiceid = aqinvoices.invoiceid
+        WHERE
+            aqbasket.booksellerid = ?
+            AND aqinvoices.invoicenumber LIKE ?
+            AND aqorders.datereceived = ? ";
+
+    my @query_params = ( $supplierid, $code, $datereceived );
+    if ( C4::Context->preference("IndependentBranches") ) {
+        unless ( C4::Context->IsSuperLibrarian() ) {
+            my $branches =
+              GetIndependentGroupModificationRights( { stringify => 1 } );
+            $strsth .= " AND ( borrowers.branchcode IN ( $branches ) OR borrowers.branchcode  = '')";
+        }
+    }
+    $strsth .= " ORDER BY aqbasket.basketno";
+    my $result_set = $dbh->selectall_arrayref(
+        $strsth,
+        { Slice => {} },
+        @query_params);
+
+    return @{$result_set};
+}
+
+#------------------------------------------------------------#
+
 =head3 GetParcels
 
   $results = &GetParcels($bookseller, $order, $code, $datefrom, $dateto);
@@ -2094,8 +2166,8 @@ sub GetLateOrders {
     }
     if (C4::Context->preference("IndependentBranches")
             && !C4::Context->IsSuperLibrarian() ) {
-        $from .= ' AND borrowers.branchcode LIKE ? ';
-        push @query_params, C4::Context->userenv->{branch};
+        my $branches = GetIndependentGroupModificationRights( { stringify => 1 } );
+        $from .= qq{ AND borrowers.branchcode IN ( $branches ) };
     }
     $from .= " AND orderstatus <> 'cancelled' ";
     my $query = "$select $from $having\nORDER BY latesince, basketno, borrowers.branchcode, supplier";
@@ -2319,11 +2391,12 @@ sub GetHistory {
     }
 
 
-    if ( C4::Context->preference("IndependentBranches") ) {
-        unless ( C4::Context->IsSuperLibrarian() ) {
-            $query .= " AND (borrowers.branchcode = ? OR borrowers.branchcode ='' ) ";
-            push @query_params, C4::Context->userenv->{branch};
-        }
+    if ( C4::Context->preference("IndependentBranches")
+        && !C4::Context->IsSuperLibrarian() )
+    {
+        my $branches =
+          GetIndependentGroupModificationRights( { stringify => 1 } );
+        $query .= qq{ AND ( borrowers.branchcode = ? OR borrowers.branchcode IN ( $branches ) ) };
     }
     $query .= " ORDER BY id";
 
diff --git a/C4/Branch.pm b/C4/Branch.pm
index ff571dc..764380a 100644
--- a/C4/Branch.pm
+++ b/C4/Branch.pm
@@ -19,6 +19,7 @@ package C4::Branch;
 use strict;
 #use warnings; FIXME - Bug 2505
 require Exporter;
+use Carp;
 use C4::Context;
 
 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
@@ -42,6 +43,7 @@ BEGIN {
 		&GetBranchCategories
 		&GetBranchesInCategory
 		&ModBranchCategoryInfo
+        &GetIndependentGroupModificationRights
 		&DelBranch
 		&DelBranchCategory
 	        &CheckCategoryUnique
@@ -113,9 +115,9 @@ sub GetBranches {
     my $sth;
     my $query = "SELECT * FROM branches";
     my @bind_parameters;
-    if ( $onlymine && C4::Context->userenv && C4::Context->userenv->{branch} ) {
-        $query .= ' WHERE branchcode = ? ';
-        push @bind_parameters, C4::Context->userenv->{branch};
+    if ($onlymine && C4::Context->userenv && C4::Context->userenv->{branch}){
+      my $branches = GetIndependentGroupModificationRights({ stringify => 1 });
+      $query .= qq{ WHERE branchcode IN ( $branches ) };
     }
     $query .= " ORDER BY branchname";
     $sth = $dbh->prepare($query);
@@ -298,7 +300,10 @@ C<$results> is an hashref
 
 sub GetBranchCategory {
     my ($catcode) = @_;
-    return unless $catcode;
+    unless ( $catcode ) {
+        carp("No category code passed in!");
+        return;
+    }
 
     my $dbh = C4::Context->dbh;
     my $sth;
@@ -368,7 +373,7 @@ the categories were already here, and minimally used.
 
 	#TODO  manage category types.  rename possibly to 'agency domains' ? as borrowergroups are called categories.
 sub GetCategoryTypes {
-	return ( 'searchdomain','properties');
+ return ( 'searchdomain','independent_groups');
 }
 
 =head2 GetBranch
@@ -423,6 +428,99 @@ sub GetBranchesInCategory {
 	return( \@branches );
 }
 
+=head2 GetIndependentGroupModificationRights
+
+    GetIndependentGroupModificationRights(
+                                           {
+                                               branch => $this_branch,
+                                               for => $other_branch,
+                                               stringify => 1,
+                                           }
+                                          );
+
+    Returns a list of branches this branch shares a common
+    independent group with.
+
+    If 'branch' is not provided, it will be looked up via
+    C4::Context->userenv->{branch}.
+
+    If 'for' is provided, the lookup is limited to that branch.
+
+    If called in a list context, returns a list of
+    branchcodes ( including $this_branch ).
+
+    If called in a scalar context, it returns
+    a count of matching branchcodes. Returns 1 if
+
+    If stringify param is passed, the return value will
+    be a string of the comma delimited branchcodes. This
+    is useful for "branchcode IN $branchcodes" clauses
+    in SQL queries.
+
+    $this_branch and $other_branch are equal for efficiency.
+
+    So you can write:
+    my @branches = GetIndependentGroupModificationRights();
+    or something like:
+    if ( GetIndependentGroupModificationRights( { for => $other_branch } ) ) { do_stuff(); }
+
+=cut
+
+sub GetIndependentGroupModificationRights {
+    my ($params) = @_;
+
+    my $this_branch  = $params->{branch};
+    my $other_branch = $params->{for};
+    my $stringify    = $params->{stringify};
+
+    $this_branch ||= C4::Context->userenv->{branch};
+
+    carp("No branch found!") unless ($this_branch);
+
+    return 1 if ( $this_branch eq $other_branch );
+
+    my $sql = q{
+        SELECT DISTINCT(branchcode)
+        FROM branchrelations
+        JOIN branchcategories USING ( categorycode )
+        WHERE categorycode IN (
+            SELECT categorycode
+            FROM branchrelations
+            WHERE branchcode = ?
+        )
+        AND branchcategories.categorytype = 'independent_group'
+    };
+
+    my @params;
+    push( @params, $this_branch );
+
+    if ($other_branch) {
+        $sql .= q{ AND branchcode = ? };
+        push( @params, $other_branch );
+    }
+
+    my $dbh = C4::Context->dbh;
+    my @branchcodes = @{ $dbh->selectcol_arrayref( $sql, {}, @params ) };
+
+    if ( $stringify ) {
+        if ( @branchcodes ) {
+            return join( ',', map { qq{'$_'} } @branchcodes );
+        } else {
+            return qq{'$this_branch'};
+        }
+    }
+
+    if ( wantarray() ) {
+        if ( @branchcodes ) {
+            return @branchcodes;
+        } else {
+            return $this_branch;
+        }
+    } else {
+        return scalar(@branchcodes);
+    }
+}
+
 =head2 GetBranchInfo
 
 $results = GetBranchInfo($branchcode);
diff --git a/C4/Circulation.pm b/C4/Circulation.pm
index 14dfb0e..1e23f9d 100644
--- a/C4/Circulation.pm
+++ b/C4/Circulation.pm
@@ -911,14 +911,25 @@ sub CanBookBeIssued {
         $alerts{ITEM_LOST} = $code if ( C4::Context->preference("IssueLostItem") eq 'alert' );
     }
     if ( C4::Context->preference("IndependentBranches") ) {
-        my $userenv = C4::Context->userenv;
         unless ( C4::Context->IsSuperLibrarian() ) {
-            if ( $item->{C4::Context->preference("HomeOrHoldingBranch")} ne $userenv->{branch} ){
+            unless (
+                GetIndependentGroupModificationRights(
+                    {
+                        for => $item->{ C4::Context->preference(
+                                "HomeOrHoldingBranch") }
+                    }
+                )
+              )
+            {
                 $issuingimpossible{ITEMNOTSAMEBRANCH} = 1;
-                $issuingimpossible{'itemhomebranch'} = $item->{C4::Context->preference("HomeOrHoldingBranch")};
+                $issuingimpossible{'itemhomebranch'} =
+                  $item->{ C4::Context->preference("HomeOrHoldingBranch") };
             }
-            $needsconfirmation{BORRNOTSAMEBRANCH} = GetBranchName( $borrower->{'branchcode'} )
-              if ( $borrower->{'branchcode'} ne $userenv->{branch} );
+
+            $needsconfirmation{BORRNOTSAMEBRANCH} =
+              GetBranchName( $borrower->{'branchcode'} )
+              if (
+                $borrower->{'branchcode'} ne C4::Context->userenv->{branch} );
         }
     }
     #
diff --git a/C4/Items.pm b/C4/Items.pm
index a225b49..c8adaf9 100644
--- a/C4/Items.pm
+++ b/C4/Items.pm
@@ -35,6 +35,7 @@ use DateTime::Format::MySQL;
 use Data::Dumper; # used as part of logging item record changes, not just for
                   # debugging; so please don't remove this
 use Koha::DateUtils qw/dt_from_string/;
+use C4::Branch qw/GetIndependentGroupModificationRights/;
 
 use Koha::Database;
 
@@ -1333,10 +1334,13 @@ sub GetItemsInfo {
     my $serial;
 
     my $userenv = C4::Context->userenv;
-    my $want_not_same_branch = C4::Context->preference("IndependentBranches") && !C4::Context->IsSuperLibrarian();
     while ( my $data = $sth->fetchrow_hashref ) {
-        if ( $data->{borrowernumber} && $want_not_same_branch) {
-            $data->{'NOTSAMEBRANCH'} = $data->{'bcode'} ne $userenv->{branch};
+        if ( C4::Context->preference("IndependentBranches") && $userenv ) {
+            unless ( C4::Context->IsSuperLibrarian()
+                || GetIndependentGroupModificationRights( { for => $data->{'bcode'} } ) )
+            {
+                $data->{'NOTSAMEBRANCH'} = $data->{'bcode'} ne $userenv->{branch};
+            }
         }
 
         $serial ||= $data->{'serial'};
@@ -2279,12 +2283,18 @@ sub DelItemCheck {
 
     my $item = GetItem($itemnumber);
 
-    if ($onloan){
-        $error = "book_on_loan" 
+    if ($onloan) {
+        $error = "book_on_loan";
     }
-    elsif ( !C4::Context->IsSuperLibrarian()
-        and C4::Context->preference("IndependentBranches")
-        and ( C4::Context->userenv->{branch} ne $item->{'homebranch'} ) )
+    elsif (
+           !C4::Context->IsSuperLibrarian()
+        && C4::Context->preference("IndependentBranches")
+        && !GetIndependentGroupModificationRights(
+            {
+                for => $item->{ C4::Context->preference("HomeOrHoldingBranch") }
+            }
+        )
+      )
     {
         $error = "not_same_branch";
     }
@@ -2959,8 +2969,9 @@ sub PrepareItemrecordDisplay {
                     if ( $tagslib->{$tag}->{$subfield}->{'authorised_value'} eq "branches" ) {
                         if (   ( C4::Context->preference("IndependentBranches") )
                             && !C4::Context->IsSuperLibrarian() ) {
-                            my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches WHERE branchcode = ? ORDER BY branchname" );
-                            $sth->execute( C4::Context->userenv->{branch} );
+                            my $branches = GetIndependentGroupModificationRights( { stringify => 1 } );
+                            my $sth = $dbh->prepare( "SELECT branchcode,branchname FROM branches WHERE branchcode IN ( $branches ) ORDER BY branchname" );
+                            $sth->execute();
                             push @authorised_values, ""
                               unless ( $tagslib->{$tag}->{$subfield}->{mandatory} );
                             while ( my ( $branchcode, $branchname ) = $sth->fetchrow_array ) {
diff --git a/C4/Letters.pm b/C4/Letters.pm
index a11c862..91c579b 100644
--- a/C4/Letters.pm
+++ b/C4/Letters.pm
@@ -209,6 +209,7 @@ sub getletter {
     my ( $module, $code, $branchcode, $message_transport_type ) = @_;
     $message_transport_type ||= 'email';
 
+    $branchcode ||= q{};
 
     if ( C4::Context->preference('IndependentBranches')
             and $branchcode
diff --git a/C4/Members.pm b/C4/Members.pm
index 4fb845d..d09b42c 100644
--- a/C4/Members.pm
+++ b/C4/Members.pm
@@ -25,6 +25,7 @@ use strict;
 use C4::Context;
 use C4::Dates qw(format_date_in_iso format_date);
 use String::Random qw( random_string );
+use Clone qw(clone);
 use Date::Calc qw/Today Add_Delta_YM check_date Date_to_Days/;
 use C4::Log; # logaction
 use C4::Overdues;
@@ -44,6 +45,7 @@ use Text::Unaccent qw( unac_string );
 use Koha::AuthUtils qw(hash_password);
 use Koha::Database;
 use Module::Load;
+use C4::Branch qw( GetIndependentGroupModificationRights );
 if ( C4::Context->preference('NorwegianPatronDBEnable') && C4::Context->preference('NorwegianPatronDBEnable') == 1 ) {
     load Koha::NorwegianPatronDB, qw( NLUpdateHashedPIN NLEncryptPIN NLSync );
 }
@@ -261,23 +263,19 @@ sub Search {
     # $showallbranches was not used at the time SearchMember() was mainstreamed into Search().
     # Mentioning for the reference
 
-    if ( C4::Context->preference("IndependentBranches") ) { # && !$showallbranches){
-        if ( my $userenv = C4::Context->userenv ) {
-            my $branch =  $userenv->{'branch'};
-            if ( !C4::Context->IsSuperLibrarian() && $branch ){
-                if (my $fr = ref $filter) {
-                    if ( $fr eq "HASH" ) {
-                        $filter->{branchcode} = $branch;
-                    }
-                    else {
-                        foreach (@$filter) {
-                            $_ = { '' => $_ } unless ref $_;
-                            $_->{branchcode} = $branch;
-                        }
-                    }
+    if ( C4::Context->preference("IndependentBranches") ) {
+        unless ( C4::Context->IsSuperLibrarian() ) {
+            $filter = clone($filter);    # Modify a copy only
+            my @branches = GetIndependentGroupModificationRights();
+            if ( my $fr = ref $filter ) {
+                if ( $fr eq "HASH" ) {
+                    $filter->{branchcode} = \@branches;
                 }
                 else {
-                    $filter = { '' => $filter, branchcode => $branch };
+                    foreach (@$filter) {
+                        $_ = { '' => $_ } unless ref $_;
+                        $_->{branchcode} = \@branches;
+                    }
                 }
             }
         }
@@ -1411,6 +1409,12 @@ sub checkuniquemember {
             ($dateofbirth) ?
             "SELECT borrowernumber,categorycode FROM borrowers WHERE surname=? and firstname=?  and dateofbirth=?" :
             "SELECT borrowernumber,categorycode FROM borrowers WHERE surname=? and firstname=?";
+
+    if ( C4::Context->preference('IndependentBranches') ) {
+        my $branches = GetIndependentGroupModificationRights( { stringify => 1 } );
+        $request .= " AND branchcode IN ( $branches )";
+    }
+
     my $sth = $dbh->prepare($request);
     if ($collectivity) {
         $sth->execute( uc($surname) );
@@ -2102,13 +2106,14 @@ sub GetBorrowersToExpunge {
     my $filterdate     = $params->{'not_borrowered_since'};
     my $filterexpiry   = $params->{'expired_before'};
     my $filtercategory = $params->{'category_code'};
-    my $filterbranch   = $params->{'branchcode'} ||
-                        ((C4::Context->preference('IndependentBranches')
-                             && C4::Context->userenv 
-                             && !C4::Context->IsSuperLibrarian()
-                             && C4::Context->userenv->{branch})
-                         ? C4::Context->userenv->{branch}
-                         : "");  
+    my $filterbranch   = $params->{'branchcode'};
+    my @filterbranches =
+      (      C4::Context->preference('IndependentBranches')
+          && C4::Context->userenv
+          && !C4::Context->IsSuperLibrarian()
+          && C4::Context->userenv->{branch} )
+      ? GetIndependentGroupModificationRights()
+      : ($filterbranch);
 
     my $dbh   = C4::Context->dbh;
     my $query = "
@@ -2123,9 +2128,10 @@ sub GetBorrowersToExpunge {
         AND borrowernumber NOT IN (SELECT guarantorid FROM borrowers WHERE guarantorid IS NOT NULL AND guarantorid <> 0)
    ";
     my @query_params;
-    if ( $filterbranch && $filterbranch ne "" ) {
-        $query.= " AND borrowers.branchcode = ? ";
-        push( @query_params, $filterbranch );
+    if ( @filterbranches ) {
+        my $placeholders = join( ',', ('?') x @filterbranches );
+        $query.= " AND borrowers.branchcode IN ( $placeholders )";
+        push( @query_params, @filterbranches );
     }
     if ( $filterexpiry ) {
         $query .= " AND dateexpiry < ? ";
@@ -2168,13 +2174,16 @@ I<$result> is a ref to an array which all elements are a hasref.
 =cut
 
 sub GetBorrowersWhoHaveNeverBorrowed {
-    my $filterbranch = shift || 
-                        ((C4::Context->preference('IndependentBranches')
-                             && C4::Context->userenv 
-                             && !C4::Context->IsSuperLibrarian()
-                             && C4::Context->userenv->{branch})
-                         ? C4::Context->userenv->{branch}
-                         : "");  
+    my $filterbranch = shift;
+
+    my @filterbranches =
+      (      C4::Context->preference('IndependentBranches')
+          && C4::Context->userenv
+          && !C4::Context->IsSuperLibrarian()
+          && C4::Context->userenv->{branch} )
+      ? GetIndependentGroupModificationRights()
+      : ($filterbranch);
+
     my $dbh   = C4::Context->dbh;
     my $query = "
         SELECT borrowers.borrowernumber,max(timestamp) as latestissue
@@ -2182,10 +2191,12 @@ sub GetBorrowersWhoHaveNeverBorrowed {
           LEFT JOIN issues ON borrowers.borrowernumber = issues.borrowernumber
         WHERE issues.borrowernumber IS NULL
    ";
+
     my @query_params;
-    if ($filterbranch && $filterbranch ne ""){ 
-        $query.=" AND borrowers.branchcode= ?";
-        push @query_params,$filterbranch;
+    if (@filterbranches) {
+        my $placeholders = join( ',', ('?') x @filterbranches );
+        $query .= " AND borrowers.branchcode IN ( $placeholders ) ";
+        push( @query_params, @filterbranches );
     }
     warn $query if $debug;
   
@@ -2218,25 +2229,32 @@ This hashref is containt the number of time this borrowers has borrowed before I
 sub GetBorrowersWithIssuesHistoryOlderThan {
     my $dbh  = C4::Context->dbh;
     my $date = shift ||POSIX::strftime("%Y-%m-%d",localtime());
-    my $filterbranch = shift || 
-                        ((C4::Context->preference('IndependentBranches')
-                             && C4::Context->userenv 
-                             && !C4::Context->IsSuperLibrarian()
-                             && C4::Context->userenv->{branch})
-                         ? C4::Context->userenv->{branch}
-                         : "");  
+    my $filterbranch = shift;
+
+    my @filterbranches =
+      (      C4::Context->preference('IndependentBranches')
+          && C4::Context->userenv
+          && !C4::Context->IsSuperLibrarian()
+          && C4::Context->userenv->{branch} )
+      ? GetIndependentGroupModificationRights()
+      : ($filterbranch);
+
     my $query = "
        SELECT count(borrowernumber) as n,borrowernumber
        FROM old_issues
        WHERE returndate < ?
          AND borrowernumber IS NOT NULL 
     "; 
+
     my @query_params;
-    push @query_params, $date;
-    if ($filterbranch){
-        $query.="   AND branchcode = ?";
-        push @query_params, $filterbranch;
-    }    
+    push( @query_params, $date );
+
+    if (@filterbranches) {
+        my $placeholders = join( ',', ('?') x @filterbranches );
+        $query .= " AND branchcode IN ( $placeholders ) ";
+        push( @query_params, @filterbranches );
+    }
+
     $query.=" GROUP BY borrowernumber ";
     warn $query if $debug;
     my $sth = $dbh->prepare($query);
diff --git a/C4/Serials.pm b/C4/Serials.pm
index 49325cb..6a5638e 100644
--- a/C4/Serials.pm
+++ b/C4/Serials.pm
@@ -31,6 +31,7 @@ use C4::Log;    # logaction
 use C4::Debug;
 use C4::Serials::Frequency;
 use C4::Serials::Numberpattern;
+use C4::Branch qw(GetIndependentGroupModificationRights);
 
 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
 
@@ -173,7 +174,22 @@ sub GetSerialInformation {
     my ($serialid) = @_;
     my $dbh        = C4::Context->dbh;
     my $query      = qq|
-        SELECT serial.*, serial.notes as sernotes, serial.status as serstatus,subscription.*,subscription.subscriptionid as subsid
+        SELECT serial.*,
+               serial.notes as sernotes,
+               serial.status as serstatus,
+               subscription.*,
+               subscription.subscriptionid as subsid
+    |;
+    if (   C4::Context->preference('IndependentBranches')
+        && C4::Context->userenv
+        && C4::Context->userenv->{'flags'} % 2 != 1
+        && C4::Context->userenv->{'branch'} ) {
+        my $branches = GetIndependentGroupModificationRights( { stringify => 1 } );
+        $query .= qq|
+            , ( ( subscription.branchcode NOT IN ( $branches ) ) AND subscription.branchcode <> '' AND subscription.branchcode IS NOT NULL ) AS cannotedit
+        |;
+    }
+    $query .= qq|
         FROM   serial LEFT JOIN subscription ON subscription.subscriptionid=serial.subscriptionid
         WHERE  serialid = ?
     |;
@@ -276,18 +292,35 @@ subscription, subscriptionhistory, aqbooksellers.name, biblio.title
 sub GetSubscription {
     my ($subscriptionid) = @_;
     my $dbh              = C4::Context->dbh;
-    my $query            = qq(
+
+    my $query = qq|
         SELECT  subscription.*,
                 subscriptionhistory.*,
                 aqbooksellers.name AS aqbooksellername,
                 biblio.title AS bibliotitle,
                 subscription.biblionumber as bibnum
+    |;
+
+    if (   C4::Context->preference('IndependentBranches')
+        && C4::Context->userenv
+        && C4::Context->userenv->{'flags'} % 2 != 1
+        && C4::Context->userenv->{'branch'} )
+    {
+        my $branches =
+          GetIndependentGroupModificationRights( { stringify => 1 } );
+
+        $query .= qq|
+            , ( ( subscription.branchcode NOT IN ( $branches ) ) AND subscription.branchcode <> '' AND subscription.branchcode IS NOT NULL ) AS cannotedit
+        |;
+    }
+
+    $query .= qq|
        FROM subscription
        LEFT JOIN subscriptionhistory ON subscription.subscriptionid=subscriptionhistory.subscriptionid
        LEFT JOIN aqbooksellers ON subscription.aqbooksellerid=aqbooksellers.id
        LEFT JOIN biblio ON biblio.biblionumber=subscription.biblionumber
        WHERE subscription.subscriptionid = ?
-    );
+    |;
 
     $debug and warn "query : $query\nsubsid :$subscriptionid";
     my $sth = $dbh->prepare($query);
@@ -310,8 +343,10 @@ sub GetFullSubscription {
     return unless ($subscriptionid);
 
     my $dbh              = C4::Context->dbh;
-    my $query            = qq|
-  SELECT    serial.serialid,
+
+    my $query = qq|
+        SELECT
+            serial.serialid,
             serial.serialseq,
             serial.planneddate, 
             serial.publisheddate, 
@@ -322,6 +357,22 @@ sub GetFullSubscription {
             biblio.title as bibliotitle,
             subscription.branchcode AS branchcode,
             subscription.subscriptionid AS subscriptionid
+    |;
+
+    if (   C4::Context->preference('IndependentBranches')
+        && C4::Context->userenv
+        && C4::Context->userenv->{'flags'} % 2 != 1
+        && C4::Context->userenv->{'branch'} )
+    {
+        my $branches =
+          GetIndependentGroupModificationRights( { stringify => 1 } );
+
+        $query .= qq|
+            , ( ( subscription.branchcode NOT IN ( $branches ) ) AND subscription.branchcode <> '' AND subscription.branchcode IS NOT NULL ) AS cannotedit
+        |;
+    }
+
+    $query .= qq|
   FROM      serial 
   LEFT JOIN subscription ON 
           (serial.subscriptionid=subscription.subscriptionid )
@@ -442,6 +493,16 @@ sub GetSubscriptionsFromBiblionumber {
         $subs->{ "periodicity" . $subs->{periodicity} }     = 1;
         $subs->{ "numberpattern" . $subs->{numberpattern} } = 1;
         $subs->{ "status" . $subs->{'status'} }             = 1;
+        $subs->{'cannotedit'} = (
+                 C4::Context->preference('IndependentBranches')
+              && C4::Context->userenv
+              && !C4::Context->IsSuperLibrarian()
+              && C4::Context->userenv->{branch}
+              && $subs->{branchcode}
+              && GetIndependentGroupModificationRights(
+                { for => $subs->{branchcode} }
+              )
+        );
 
         if ( $subs->{enddate} eq '0000-00-00' ) {
             $subs->{enddate} = '';
@@ -466,8 +527,10 @@ sub GetSubscriptionsFromBiblionumber {
 sub GetFullSubscriptionsFromBiblionumber {
     my ($biblionumber) = @_;
     my $dbh            = C4::Context->dbh;
-    my $query          = qq|
-  SELECT    serial.serialid,
+
+    my $query = qq|
+        SELECT
+            serial.serialid,
             serial.serialseq,
             serial.planneddate, 
             serial.publisheddate, 
@@ -477,6 +540,22 @@ sub GetFullSubscriptionsFromBiblionumber {
             biblio.title as bibliotitle,
             subscription.branchcode AS branchcode,
             subscription.subscriptionid AS subscriptionid
+    |;
+
+    if (   C4::Context->preference('IndependentBranches')
+        && C4::Context->userenv
+        && C4::Context->userenv->{'flags'} != 1
+        && C4::Context->userenv->{'branch'} )
+    {
+        my $branches =
+          GetIndependentGroupModificationRights( { stringify => 1 } );
+
+        $query .= qq|
+            , ( ( subscription.branchcode NOT IN ( $branches ) ) AND subscription.branchcode <> '' AND subscription.branchcode IS NOT NULL ) AS cannotedit
+        |;
+    }
+
+    $query .= qq|
   FROM      serial 
   LEFT JOIN subscription ON 
           (serial.subscriptionid=subscription.subscriptionid)
@@ -2762,7 +2841,9 @@ sub can_show_subscription {
 sub _can_do_on_subscription {
     my ( $subscription, $userid, $permission ) = @_;
     return 0 unless C4::Context->userenv;
+
     my $flags = C4::Context->userenv->{flags};
+
     $userid ||= C4::Context->userenv->{'id'};
 
     if ( C4::Context->preference('IndependentBranches') ) {
@@ -2771,12 +2852,16 @@ sub _can_do_on_subscription {
               or
               C4::Auth::haspermission( $userid, { serials => 'superserials' } )
               or (
-                  C4::Auth::haspermission( $userid,
-                      { serials => $permission } )
+                  C4::Auth::haspermission(
+                      $userid, { serials => $permission }
+                  )
                   and (  not defined $subscription->{branchcode}
                       or $subscription->{branchcode} eq ''
                       or $subscription->{branchcode} eq
                       C4::Context->userenv->{'branch'} )
+              )
+              or GetIndependentGroupModificationRights(
+                  { for => $subscription->{branchcode} }
               );
     }
     else {
@@ -2784,10 +2869,8 @@ sub _can_do_on_subscription {
           if C4::Context->IsSuperLibrarian()
               or
               C4::Auth::haspermission( $userid, { serials => 'superserials' } )
-              or C4::Auth::haspermission(
-                  $userid, { serials => $permission }
-              ),
-        ;
+              or C4::Auth::haspermission( $userid, { serials => $permission } )
+            ,;
     }
     return 0;
 }
diff --git a/C4/Suggestions.pm b/C4/Suggestions.pm
index f34c942..71c35c1 100644
--- a/C4/Suggestions.pm
+++ b/C4/Suggestions.pm
@@ -33,6 +33,7 @@ use Koha::DateUtils qw( dt_from_string );
 use List::MoreUtils qw(any);
 use C4::Dates qw(format_date_in_iso);
 use base qw(Exporter);
+use C4::Branch qw(GetIndependentGroupModificationRights);
 
 our $VERSION = 3.07.00.049;
 our @EXPORT  = qw(
@@ -133,18 +134,15 @@ sub SearchSuggestion {
     }
 
     # filter on user branch
-    if ( C4::Context->preference('IndependentBranches') ) {
-        my $userenv = C4::Context->userenv;
-        if ($userenv) {
-            if ( !C4::Context->IsSuperLibrarian() && !$suggestion->{branchcode} )
-            {
-                push @sql_params, $$userenv{branch};
-                push @query,      q{
-                    AND (suggestions.branchcode=? OR suggestions.branchcode='')
-                };
-            }
-        }
-    } else {
+    if (   C4::Context->preference('IndependentBranches')
+        && !C4::Context->IsSuperLibrarian()
+        && !$suggestion->{branchcode} )
+    {
+        my $branches =
+          GetIndependentGroupModificationRights( { stringify => 1 } );
+        push( @query, qq{ AND (suggestions.branchcode IN ( $branches ) OR suggestions.branchcode='') } );
+    }
+    else {
         if ( defined $suggestion->{branchcode} && $suggestion->{branchcode} ) {
             unless ( $suggestion->{branchcode} eq '__ANY__' ) {
                 push @sql_params, $suggestion->{branchcode};
@@ -342,13 +340,19 @@ sub GetSuggestionByStatus {
 
     # filter on branch
     if ( C4::Context->preference("IndependentBranches") || $branchcode ) {
-        my $userenv = C4::Context->userenv;
-        if ($userenv) {
-            unless ( C4::Context->IsSuperLibrarian() ) {
-                push @sql_params, $userenv->{branch};
-                $query .= q{ AND (U1.branchcode = ? OR U1.branchcode ='') };
-            }
+        if (   C4::Context->userenv
+            && C4::Context->preference("IndependentBranches")
+            && !C4::Context->IsSuperLibrarian() )
+        {
+
+            my $branches =
+              GetIndependentGroupModificationRights( { stringify => 1 } );
+
+            $query .= qq{
+                AND (U1.branchcode IN ( $branches ) OR U1.branchcode ='')
+            };
         }
+
         if ($branchcode) {
             push @sql_params, $branchcode;
             $query .= q{ AND (U1.branchcode = ? OR U1.branchcode ='') };
@@ -394,12 +398,19 @@ sub CountSuggestion {
     if ( C4::Context->preference("IndependentBranches")
         && !C4::Context->IsSuperLibrarian() )
     {
-        my $query = q{
+        my $branches =
+          GetIndependentGroupModificationRights( { stringify => 1 } );
+
+        my $query = qq{
             SELECT count(*)
             FROM suggestions
                 LEFT JOIN borrowers ON borrowers.borrowernumber=suggestions.suggestedby
             WHERE STATUS=?
-                AND (borrowers.branchcode='' OR borrowers.branchcode=?)
+                AND (
+                    borrowers.branchcode IN ( $branches )
+                    OR
+                    borrowers.branchcode=?
+                )
         };
         $sth = $dbh->prepare($query);
         $sth->execute( $status, $userenv->{branch} );
diff --git a/acqui/basket.pl b/acqui/basket.pl
index 921c67e..8e82b04 100755
--- a/acqui/basket.pl
+++ b/acqui/basket.pl
@@ -36,6 +36,7 @@ use C4::Members qw/GetMember/;  #needed for permissions checking for changing ba
 use C4::Items;
 use C4::Suggestions;
 use Date::Calc qw/Add_Delta_Days/;
+use C4::Branch qw/GetIndependentGroupModificationRights/;
 
 =head1 NAME
 
@@ -155,10 +156,17 @@ if ( $op eq 'delete_confirm' ) {
     if ( C4::Context->preference("IndependentBranches") ) {
         my $userenv = C4::Context->userenv;
         unless ( C4::Context->IsSuperLibrarian() ) {
-            my $validtest = ( $basket->{creationdate} eq '' )
+            my $validtest =
+                 ( $basket->{creationdate} eq '' )
               || ( $userenv->{branch} eq $basket->{branch} )
               || ( $userenv->{branch} eq '' )
-              || ( $basket->{branch}  eq '' );
+              || ( $basket->{branch}  eq '' )
+              || (
+                GetIndependentGroupModificationRights(
+                    { for => $basket->{branch} }
+                )
+              );
+
             unless ($validtest) {
                 print $query->redirect("../mainpage.pl");
                 exit 1;
@@ -257,10 +265,16 @@ if ( $op eq 'delete_confirm' ) {
     if ( C4::Context->preference("IndependentBranches") ) {
         my $userenv = C4::Context->userenv;
         unless ( C4::Context->IsSuperLibrarian() ) {
-            my $validtest = ( $basket->{creationdate} eq '' )
+            my $validtest =
+                 ( $basket->{creationdate} eq '' )
               || ( $userenv->{branch} eq $basket->{branch} )
               || ( $userenv->{branch} eq '' )
-              || ( $basket->{branch}  eq '' );
+              || ( $basket->{branch}  eq '' )
+              || (
+                GetIndependentGroupModificationRights(
+                    { for => $basket->{branch} }
+                )
+              );
             unless ($validtest) {
                 print $query->redirect("../mainpage.pl");
                 exit 1;
diff --git a/admin/branches.pl b/admin/branches.pl
index a698a26..ac988c6 100755
--- a/admin/branches.pl
+++ b/admin/branches.pl
@@ -258,7 +258,7 @@ sub editbranchform {
         $oldprinter = $data->{'branchprinter'} || '';
         _branch_to_template($data, $innertemplate);
     }
-    $innertemplate->param( categoryloop => $catinfo );
+    $innertemplate->param( branch_categories  => $catinfo );
 
     foreach my $thisprinter ( keys %$printers ) {
         push @printerloop, {
@@ -277,25 +277,8 @@ sub editbranchform {
 }
 
 sub editcatform {
-
-    # prepares the edit form...
-    my ($categorycode,$innertemplate) = @_;
-    # warn "cat : $categorycode";
-	my @cats;
-    my $data;
-	if ($categorycode) {
-        my $data = GetBranchCategory($categorycode);
-        $innertemplate->param(
-            categorycode    => $data->{'categorycode'},
-            categoryname    => $data->{'categoryname'},
-            codedescription => $data->{'codedescription'},
-            show_in_pulldown => $data->{'show_in_pulldown'},
-		);
-    }
-	for my $ctype (GetCategoryTypes()) {
-		push @cats , { type => $ctype , selected => ($data->{'categorytype'} and $data->{'categorytype'} eq $ctype) };
-	}
-    $innertemplate->param(categorytype => \@cats);
+    my ( $categorycode, $innertemplate ) = @_;
+    $innertemplate->param( category => GetBranchCategory($categorycode) );
 }
 
 sub branchinfotable {
@@ -366,25 +349,20 @@ sub branchinfotable {
 
         push @loop_data, \%row;
     }
-    my @branchcategories = ();
-	for my $ctype ( GetCategoryTypes() ) {
-        my $catinfo = GetBranchCategories($ctype);
-        my @categories;
-		foreach my $cat (@$catinfo) {
-            push @categories, {
-                categoryname    => $cat->{'categoryname'},
-                categorycode    => $cat->{'categorycode'},
-                codedescription => $cat->{'codedescription'},
-                categorytype    => $cat->{'categorytype'},
-            };
-    	}
-        push @branchcategories, { categorytype => $ctype , $ctype => 1 , catloop => ( @categories ? \@categories : undef) };
-	}
+
+    my $catinfo = GetBranchCategories();
+    my $categories;
+    foreach my $cat (@$catinfo) {
+        $categories->{ $cat->{categorytype} }->{ $cat->{'categorycode'} } = {
+            categoryname    => $cat->{'categoryname'},
+            codedescription => $cat->{'codedescription'},
+        };
+    }
+
     $innertemplate->param(
-        branches         => \@loop_data,
-        branchcategories => \@branchcategories
+        branches          => \@loop_data,
+        branch_categories => $categories
     );
-
 }
 
 sub _branch_to_template {
diff --git a/catalogue/moredetail.pl b/catalogue/moredetail.pl
index 39d3c9f..ca48237 100755
--- a/catalogue/moredetail.pl
+++ b/catalogue/moredetail.pl
@@ -176,13 +176,18 @@ foreach my $item (@items){
         $item->{status_advisory} = 1;
     }
 
-    if (C4::Context->preference("IndependentBranches")) {
-        #verifying rights
-        my $userenv = C4::Context->userenv();
-        unless (C4::Context->IsSuperLibrarian() or ($userenv->{'branch'} eq $item->{'homebranch'})) {
-                $item->{'nomod'}=1;
+    if ( C4::Context->preference("IndependentBranches") ) {
+        unless (
+            C4::Context->IsSuperLibrarian()
+            || GetIndependentGroupModificationRights(
+                { for => $item->{'homebranch'} }
+            )
+          )
+        {
+            $item->{'nomod'} = 1;
         }
     }
+
     $item->{'homebranchname'} = GetBranchName($item->{'homebranch'});
     $item->{'holdingbranchname'} = GetBranchName($item->{'holdingbranch'});
     if ($item->{'datedue'}) {
diff --git a/cataloguing/additem.pl b/cataloguing/additem.pl
index 9c7fb3f..6c01bd5 100755
--- a/cataloguing/additem.pl
+++ b/cataloguing/additem.pl
@@ -731,10 +731,19 @@ foreach my $field (@fields) {
 						|| $subfieldvalue;
         }
 
-        if (($field->tag eq $branchtagfield) && ($subfieldcode eq $branchtagsubfield) && C4::Context->preference("IndependentBranches")) {
+        if (   $field->tag eq $branchtagfield
+            && $subfieldcode eq $branchtagsubfield
+            && C4::Context->preference("IndependentBranches") )
+        {
             #verifying rights
             my $userenv = C4::Context->userenv();
-            unless (C4::Context->IsSuperLibrarian() or (($userenv->{'branch'} eq $subfieldvalue))){
+            unless (
+                C4::Context->IsSuperLibrarian()
+                || GetIndependentGroupModificationRights(
+                    { for => $subfieldvalue }
+                )
+              )
+            {
                 $this_row{'nomod'} = 1;
             }
         }
diff --git a/circ/circulation-home.pl b/circ/circulation-home.pl
index bbb715b..1a78bc5 100755
--- a/circ/circulation-home.pl
+++ b/circ/circulation-home.pl
@@ -23,6 +23,7 @@ use C4::Auth;
 use C4::Output;
 use C4::Context;
 use C4::Koha;
+use C4::Branch qw/GetIndependentGroupModificationRights/;
 
 my $query = new CGI;
 my ($template, $loggedinuser, $cookie, $flags) = get_template_and_user(
@@ -40,8 +41,10 @@ my $fa = getframeworkinfo('FA');
 $template->param( fast_cataloging => 1 ) if (defined $fa);
 
 # Checking if the transfer page needs to be displayed
-$template->param( display_transfer => 1 ) if ( ($flags->{'superlibrarian'} == 1) || (C4::Context->preference("IndependentBranches") == 0) );
-$template->{'VARS'}->{'AllowOfflineCirculation'} = C4::Context->preference('AllowOfflineCirculation');
+$template->param( display_transfer => 1 )
+  if ( $flags->{'superlibrarian'} == 1
+    || scalar GetIndependentGroupModificationRights() );
 
+$template->{'VARS'}->{'AllowOfflineCirculation'} = C4::Context->preference('AllowOfflineCirculation');
 
 output_html_with_http_headers $query, $cookie, $template->output;
diff --git a/circ/pendingreserves.pl b/circ/pendingreserves.pl
index f964074..d95efa9 100755
--- a/circ/pendingreserves.pl
+++ b/circ/pendingreserves.pl
@@ -36,6 +36,7 @@ use C4::Auth;
 use C4::Dates qw/format_date format_date_in_iso/;
 use C4::Debug;
 use Date::Calc qw/Today Add_Delta_YMD/;
+use C4::Branch qw/GetIndependentGroupModificationRights/;
 
 my $input = new CGI;
 my $startdate=$input->param('from');
@@ -153,8 +154,8 @@ if ( $run_report ) {
 
 
     if (C4::Context->preference('IndependentBranches')){
-        $strsth .= " AND items.holdingbranch=? ";
-        push @query_params, C4::Context->userenv->{'branch'};
+        my $branches = GetIndependentGroupModificationRights( { stringify => 1 } );
+        $strsth .= " AND items.holdingbranch IN ( $branches ) ";
     }
     $strsth .= " GROUP BY reserves.biblionumber ORDER BY biblio.title ";
 
diff --git a/circ/reserveratios.pl b/circ/reserveratios.pl
index c0e0062..dbff959 100755
--- a/circ/reserveratios.pl
+++ b/circ/reserveratios.pl
@@ -124,9 +124,9 @@ my $strsth =
  $sqldatewhere
 ";
 
-if (C4::Context->preference('IndependentBranches')){
-    $strsth .= " AND items.holdingbranch=? ";
-    push @query_params, C4::Context->userenv->{'branch'};
+if ( C4::Context->preference('IndependentBranches') ) {
+    my $branches = GetIndependentGroupModificationRights( { stringify => 1 } );
+    $strsth .= " AND items.holdingbranch IN ( $branches ) ";
 }
 
 $strsth .= " GROUP BY reserves.biblionumber ORDER BY reservecount DESC";
diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql
index cd83f99..e9aefd6 100644
--- a/installer/data/mysql/kohastructure.sql
+++ b/installer/data/mysql/kohastructure.sql
@@ -383,7 +383,7 @@ CREATE TABLE `branchcategories` ( -- information related to library/branch group
   `categorycode` varchar(10) NOT NULL default '', -- unique identifier for the library/branch group
   `categoryname` varchar(32), -- name of the library/branch group
   `codedescription` mediumtext, -- longer description of the library/branch group
-  `categorytype` varchar(16), -- says whether this is a search group or a properties group
+  `categorytype` ENUM(  'searchdomain',  'independent_group' ) NULL DEFAULT NULL, -- says whether this is a search group or an independent group
   `show_in_pulldown` tinyint(1) NOT NULL DEFAULT '0', -- says this group should be in the opac libararies pulldown if it is enabled
   PRIMARY KEY  (`categorycode`),
   KEY `show_in_pulldown` (`show_in_pulldown`)
diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl
index ad8ec97..ed2ca2d 100755
--- a/installer/data/mysql/updatedatabase.pl
+++ b/installer/data/mysql/updatedatabase.pl
@@ -6938,7 +6938,7 @@ $DBversion = "3.13.00.002";
 if ( CheckVersion($DBversion) ) {
    $dbh->do("UPDATE systempreferences SET variable = 'IndependentBranches' WHERE variable = 'IndependantBranches'");
    print "Upgrade to $DBversion done (Bug 10080 - Change system pref IndependantBranches to IndependentBranches)\n";
-   SetVersion ($DBversion);
+    SetVersion ($DBversion);
 }
 
 $DBversion = '3.13.00.003';
@@ -9773,6 +9773,22 @@ if ( CheckVersion($DBversion) ) {
     SetVersion ($DBversion);
 }
 
+$DBversion = "3.17.00.XXX";
+if ( CheckVersion($DBversion) ) {
+    $dbh->do(q{
+            DELETE FROM branchcategories WHERE categorytype = 'properties'
+    });
+
+    $dbh->do(q{
+        ALTER TABLE branchcategories
+        CHANGE categorytype categorytype
+          ENUM( 'searchdomain', 'independent_group' )
+            NULL DEFAULT NULL
+    });
+    print "Upgrade to $DBversion done (Remove branch property groups, add independent groups)\n";
+    SetVersion ($DBversion);
+}
+
 =head1 FUNCTIONS
 
 =head2 TableExists($table)
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branches.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branches.tt
index 887b81f..8c17d12 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branches.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branches.tt
@@ -112,20 +112,50 @@ tinyMCE.init({
         </li>
 	</ol>
 	</fieldset>
-	[% IF ( categoryloop ) %]<fieldset class="rows"><legend>Group(s):</legend>
-        <ol>
-		[% FOREACH categoryloo IN categoryloop %]
-            <li><label for="[% categoryloo.categorycode %]">[% categoryloo.categoryname %]: </label>
-                [% IF categoryloo.selected %]
-                    <input type="checkbox" id="[% categoryloo.categorycode %]" name="[% categoryloo.categorycode %]" checked="checked" />
-                [% ELSE %]
-                    <input type="checkbox" id="[% categoryloo.categorycode %]" name="[% categoryloo.categorycode %]" />
-                [% END %]
-                <span class="hint">[% categoryloo.codedescription %]</span>
-            </li>
-        [% END %]
-		</ol>
-</fieldset>[% END %]
+
+     [% IF ( branch_categories ) %]
+        <fieldset class="rows">
+            <legend>Group(s):</legend>
+            <ol>
+                <fieldset>
+                    <legend>Search domain</legend>
+                    [% FOREACH bc IN branch_categories %]
+                        [% IF bc.categorytype == "searchdomain" %]
+                            <li>
+                                <label for="[% bc.categorycode %]">[% bc.categoryname %]: </label>
+                                [% IF ( bc.selected ) %]
+                                    <input type="checkbox" id="[% bc.categorycode %]" name="[% bc.categorycode %]" checked="checked" />
+                                [% ELSE %]
+                                    <input type="checkbox" id="[% bc.categorycode %]" name="[% bc.categorycode %]" />
+                                [% END %]
+                                <span class="hint">[% bc.codedescription %]</span>
+                            </li>
+                        [% END %]
+                    [% END %]
+                </fieldset>
+            </ol>
+            <ol>
+                <fieldset>
+                    <legend>Independent library groups</legend>
+                    [% FOREACH bc IN branch_categories %]
+                        [% IF bc.categorytype == "independent_group" %]
+                            <li>
+                                <label for="[% bc.categorycode %]">[% bc.categoryname %]: </label>
+                                [% IF ( bc.selected ) %]
+                                    <input type="checkbox" id="[% bc.categorycode %]" name="[% bc.categorycode %]" checked="checked" />
+                                [% ELSE %]
+                                    <input type="checkbox" id="[% bc.categorycode %]" name="[% bc.categorycode %]" />
+                                [% END %]
+                                <span class="hint">[% bc.codedescription %]</span>
+                            </li>
+                        [% END %]
+                    [% END %]
+                <li><label>Note:</label><span class="hint">If independent library groups are enabled ( via the IndependentBranches system preference ), a library may access and alter records and patrons from libraries in any group that also library belongs to. A library may belong to multiple library groups.</span></li>
+                </fieldset>
+            </ol>
+        </fieldset>
+    [% END %]
+
 	<fieldset class="rows">
 	<ol>
         <li><label for="branchaddress1">Address line 1: </label><input type="text" name="branchaddress1" id="branchaddress1" size="60" value="[% branchaddress1 |html %]" /></li>
@@ -260,89 +290,121 @@ tinyMCE.init({
 	<div class="dialog message">There are no libraries defined. <a href="/cgi-bin/koha/admin/branches.pl?op=add">Start defining libraries</a>.</div>
 	[% END %]
 
-   [% IF ( branchcategories ) %]
-   [% FOREACH branchcategorie IN branchcategories %]
-    <h3>Group(s):  [% IF ( branchcategorie.properties ) %]Properties[% ELSE %][% IF ( branchcategorie.searchdomain ) %]Search domain[% END %][% END %]</h3>
-    [% IF ( branchcategorie.catloop ) %]
-      <table>
-        <thead>
-          <tr>
-            <th>Name</th>
-            <th>Code</th>
-            <th>Description</th>
-            <th>&nbsp;</th>
-            <th>&nbsp;</th>
-          </tr>
-        </thead>
-        <tbody>
-          [% FOREACH catloo IN branchcategorie.catloop %]
-            <tr>
-              <td>[% catloo.categoryname %]</td>
-              <td>[% catloo.categorycode %]</td>
-              <td>[% catloo.codedescription %]</td>
-              <td>
-                <a href="[% catloo.action %]?op=editcategory&amp;categorycode=[% catloo.categorycode |url %]">Edit</a>
-              </td>
-              <td>
-                <a href="[% catloo.action %]?op=delete_category&amp;categorycode=[% catloo.categorycode |url %]">Delete</a>
-              </td>
-            </tr>
-          [% END %]
-        </tbody>
-      </table>
+    <h3>Search domain groups</h3>
+    [% IF branch_categories.searchdomain %]
+        <table>
+            <thead>
+                <tr>
+                    <th>Name</th>
+                    <th>Code</th>
+                    <th>Description</th>
+                    <th>&nbsp;</th>
+                    <th>&nbsp;</th>
+                  </tr>
+            </thead>
+            <tbody>
+                [% FOREACH bc IN branch_categories.searchdomain %]
+                    <tr>
+                      <td>[% bc.value.categoryname %]</td>
+                      <td>[% bc.key %]</td>
+                      <td>[% bc.value.codedescription %]</td>
+                      <td>
+                        <a href="?op=editcategory&amp;categorycode=[% bc.key |url %]">Edit</a>
+                      </td>
+                      <td>
+                        <a href="?op=delete_category&amp;categorycode=[% bc.key |url %]">Delete</a>
+                      </td>
+                    </tr>
+                [% END %]
+            </tbody>
+        </table>
+    [% ELSE %]
+        No search domain groups defined.
+    [% END %]
+    <a href="/cgi-bin/koha/admin/branches.pl?op=editcategory">Add a new group</a>.
+
+    <h3>Independent library groups:</h3>
+    [% IF branch_categories.independent_group %]
+        <table>
+            <thead>
+                <tr>
+                    <th>Name</th>
+                    <th>Code</th>
+                    <th>Description</th>
+                    <th>&nbsp;</th>
+                    <th>&nbsp;</th>
+                  </tr>
+            </thead>
+            <tbody>
+                [% FOREACH bc IN branch_categories.independent_group %]
+                    <tr>
+                      <td>[% bc.value.categoryname %]</td>
+                      <td>[% bc.key %]</td>
+                      <td>[% bc.value.codedescription %]</td>
+                      <td>
+                        <a href="?op=editcategory&amp;categorycode=[% bc.key |url %]">Edit</a>
+                      </td>
+                      <td>
+                        <a href="?op=delete_category&amp;categorycode=[% bc.key |url %]">Delete</a>
+                      </td>
+                    </tr>
+                [% END %]
+            </tbody>
+        </table>
     [% ELSE %]
-      No [% IF ( branchcategorie.properties ) %]properties[% ELSIF ( branchcategorie.searchdomain ) %]search domain[% END %] defined. <a href="/cgi-bin/koha/admin/branches.pl?op=editcategory">Add a new group</a>.
+        No independent library groups defined.
     [% END %]
-  [% END %]
-  [% ELSE %]
-    <p>No groups defined.</p>
-  [% END %] <!-- NAME="branchcategories" -->
+    <a href="/cgi-bin/koha/admin/branches.pl?op=editcategory">Add a new group</a>.
 [% END %]
 
 [% IF ( editcategory ) %]
-    <h3>[% IF ( categorycode ) %]Edit group [% categorycode %][% ELSE %]Add group[% END %]</h3>
+    <h3>[% IF ( category ) %]Edit group [% category.categorycode %][% ELSE %]Add group[% END %]</h3>
     <form action="[% action %]" name="Aform" method="post">
     <input type="hidden" name="op" value="addcategory_validate" />
-	[% IF ( categorycode ) %]
-	<input type="hidden" name="add" value="0">
-	[% ELSE %]
-	<input type="hidden" name="add" value="1">
-	[% END %]
+    [% IF ( category.categorycode ) %]
+        <input type="hidden" name="add" value="0">
+    [% ELSE %]
+        <input type="hidden" name="add" value="1">
+    [% END %]
     <fieldset class="rows">
 
         <ol><li>
-                [% IF ( categorycode ) %]
+                [% IF ( category.categorycode ) %]
 				<span class="label">Category code: </span>
-                    <input type="hidden" name="categorycode" id="categorycode" value="[% categorycode |html %]" />
-                    [% categorycode %]
+                    <input type="hidden" name="categorycode" id="categorycode" value="[% category.categorycode | html %]" />
+                    [% category.categorycode %]
                 [% ELSE %]
-                <label for="categorycode">Category code:</label>
-                    <input type="text" name="categorycode" id="categorycode" size="10" maxlength="10" value="[% categorycode |html %]" />
+                    <label for="categorycode">Category code:</label>
+                    <input type="text" name="categorycode" id="categorycode" size="10" maxlength="10" value="[% categorycode | html %]" />
                 [% END %]
             </li>
         <li>
             <label for="categoryname">Name: </label>
-            <input type="text" name="categoryname" id="categoryname" size="32" maxlength="32" value="[% categoryname |html %]" />
+            <input type="text" name="categoryname" id="categoryname" size="32" maxlength="32" value="[% category.categoryname | html %]" />
         </li>
         <li>
             <label for="codedescription">Description: </label>
-            <input type="text" name="codedescription" id="codedescription" size="70" value="[% codedescription |html %]" />
+            <input type="text" name="codedescription" id="codedescription" size="70" value="[% category.codedescription | html %]" />
         </li>
 		<li>
         <label for="categorytype">Category type: </label>
             <select id="categorytype" name="categorytype">
-            [% FOREACH categorytyp IN categorytype %]
-                [% IF ( categorytyp.selected ) %]
-                    <option value="[% categorytyp.type %]" selected="selected">
+                [% IF ( category.categorytype == 'searchdomain' ) %]
+                    <option value="searchdomain" selected="selected">Search domain</option>
                 [% ELSE %]
-                    <option value="[% categorytyp.type %]">
-                [% END %] [% categorytyp.type %]</option>
-            [% END %]
+                    <option value="searchdomain">Search domain</option>
+                [% END %]
+
+                [% IF ( category.categorytype == 'independent_group' ) %]
+                    <option value="independent_group" selected="selected">Independent group</option>
+                [% ELSE %]
+                    <option value="independent_group">Independent group</option>
+                [% END %]
             </select>
 		</li>
         <li>
             <label for="show_in_pulldown">Show in search pulldown: </label>
-            [% IF ( show_in_pulldown ) %]
+            [% IF ( category.show_in_pulldown ) %]
                 <input type="checkbox" name="show_in_pulldown" id="show_in_pulldown" checked="checked"/>
             [% ELSE %]
                 <input type="checkbox" name="show_in_pulldown" id="show_in_pulldown" />
@@ -350,7 +412,13 @@ tinyMCE.init({
         </li>
 		</ol>
     </fieldset>
-	<fieldset class="action"><input type="submit" value="Update" /></fieldset>
+  <fieldset class="action">
+        [% IF category %]
+            <input type="submit" value="Update group" />
+        [% ELSE %]
+            <input type="submit" value="Add group" />
+        [% END %]
+    </fieldset>
     </form>
 [% END %]
 
diff --git a/members/deletemem.pl b/members/deletemem.pl
index bad636c..7d8fcc4 100755
--- a/members/deletemem.pl
+++ b/members/deletemem.pl
@@ -85,11 +85,17 @@ if ($bor->{category_type} eq "S") {
     }
 }
 
-if (C4::Context->preference("IndependentBranches")) {
-    my $userenv = C4::Context->userenv;
+if ( C4::Context->preference("IndependentBranches") ) {
     if ( !C4::Context->IsSuperLibrarian() && $bor->{'branchcode'}){
-        unless ($userenv->{branch} eq $bor->{'branchcode'}){
-            print $input->redirect("/cgi-bin/koha/members/moremember.pl?borrowernumber=$member&error=CANT_DELETE_OTHERLIBRARY");
+        unless (
+            GetIndependentGroupModificationRights(
+                { for => $bor->{'branchcode'} }
+            )
+          )
+        {
+            print $input->redirect(
+                "/cgi-bin/koha/members/moremember.pl?borrowernumber=$member&error=CANT_DELETE_OTHERLIBRARY"
+            );
             exit;
         }
     }
diff --git a/t/db_dependent/Branch.t b/t/db_dependent/Branch.t
index 9ca3f56..e692c78 100644
--- a/t/db_dependent/Branch.t
+++ b/t/db_dependent/Branch.t
@@ -21,7 +21,7 @@ use Modern::Perl;
 use C4::Context;
 use Data::Dumper;
 
-use Test::More tests => 36;
+use Test::More tests => 69;
 
 use C4::Branch;
 
@@ -178,14 +178,14 @@ my $cat1 = {
     categorycode     => 'CAT1',
     categoryname     => 'catname1',
     codedescription  => 'catdesc1',
-    categorytype     => 'cattype1',
+    categorytype     => 'searchdomain',
     show_in_pulldown => 1
 };
 my $cat2 = {
     add              => 1,
     categorycode     => 'CAT2',
     categoryname     => 'catname2',
-    categorytype     => 'catype2',
+    categorytype     => 'searchdomain',
     codedescription  => 'catdesc2',
     show_in_pulldown => 1
 };
@@ -194,7 +194,7 @@ my %new_category = (
     categorycode     => 'LIBCATCODE',
     categoryname     => 'library category name',
     codedescription  => 'library category code description',
-    categorytype     => 'searchdomain',
+    categorytype     => 'independent_group',
     show_in_pulldown => 1,
 );
 
@@ -343,7 +343,7 @@ is( CheckCategoryUnique('CAT_NO_EXISTS'), 1, 'CAT_NO_EXISTS doesnt exist' );
 
 #Test GetCategoryTypes
 my @category_types = GetCategoryTypes();
-is_deeply(\@category_types, [ 'searchdomain', 'properties' ], 'received expected library category types');
+is_deeply(\@category_types, [ 'searchdomain', 'independent_groups' ], 'received expected library category types');
 
 $categories = GetBranchCategories(undef, undef, 'LIBCATCODE');
 is_deeply($categories, [ {%$cat1}, {%$cat2},{ %new_category, selected => 1 } ], 'retrieve expected, eselected library category (bug 10515)');
@@ -355,6 +355,72 @@ is_deeply($categories, [ {%$cat1}, {%$cat2},{ %new_category, selected => 1 } ],
 my $loop = GetBranchesLoop;
 is( scalar(@$loop), GetBranchesCount(), 'There is the right number of branches' );
 
+# Test GetIndependentGroupModificationRights
+my @branches_bra = GetIndependentGroupModificationRights({ branch => 'BRA' });
+is_deeply( \@branches_bra, [ 'BRA' ], 'Library with no group only has rights for its own branch' );
+
+my $string = GetIndependentGroupModificationRights({ branch => 'BRA', stringify => 1 });
+ok( $string eq q{'BRA'}, "String returns correctly" );
+
+ok( GetIndependentGroupModificationRights({ branch => 'BRA', for => 'BRA' }), 'Boolean test for BRA rights to BRA returns true' );
+ok( !GetIndependentGroupModificationRights({ branch => 'BRA', for => 'BRB' }), 'Boolean test for BRA rights to BRB returns false' );
+ok( !GetIndependentGroupModificationRights({ branch => 'BRA', for => 'BRC' }), 'Boolean test for BRA rights to BRC returns false' );
+ok( GetIndependentGroupModificationRights({ branch => 'BRB', for => 'BRB' }), 'Boolean test for BRB rights to BRB returns true' );
+ok( !GetIndependentGroupModificationRights({ branch => 'BRB', for => 'BRA' }), 'Boolean test for BRB rights to BRA returns false' );
+ok( !GetIndependentGroupModificationRights({ branch => 'BRB', for => 'BRC' }), 'Boolean test for BRB rights to BRC returns false' );
+ok( GetIndependentGroupModificationRights({ branch => 'BRC', for => 'BRC' }), 'Boolean test for BRC rights to BRC returns true' );
+ok( !GetIndependentGroupModificationRights({ branch => 'BRC', for => 'BRA'}), 'Boolean test for BRC rights to BRA returns false' );
+ok( !GetIndependentGroupModificationRights({ branch => 'BRC', for => 'BRB'}), 'Boolean test for BRC rights to BRB returns false' );
+
+ModBranch({
+    branchcode     => 'BRA',
+    branchname     => 'BranchA',
+    LIBCATCODE     => 1,
+});
+ModBranch({
+    branchcode     => 'BRB',
+    branchname     => 'BranchB',
+    LIBCATCODE     => 1,
+});
+
+@branches_bra = GetIndependentGroupModificationRights({ branch => 'BRA' });
+is_deeply( \@branches_bra, [ 'BRA', 'BRB' ], 'Libraries in LIBCATCODE returned correctly' );
+
+$string = GetIndependentGroupModificationRights({ branch => 'BRA', stringify => 1 });
+ok( $string eq q{'BRA','BRB'}, "String returns correctly" );
+
+ok( GetIndependentGroupModificationRights({ branch => 'BRA', for => 'BRA' }), 'Boolean test for BRA rights to BRA returns true' );
+ok( GetIndependentGroupModificationRights({ branch => 'BRA', for => 'BRB'}), 'Boolean test for BRA rights to BRB returns true' );
+ok( !GetIndependentGroupModificationRights({ branch => 'BRA', for => 'BRC'}), 'Boolean test for BRA rights to BRC returns false' );
+ok( GetIndependentGroupModificationRights({ branch => 'BRB', for => 'BRB' }), 'Boolean test for BRB rights to BRB returns true' );
+ok( GetIndependentGroupModificationRights({ branch => 'BRB', for => 'BRA'}), 'Boolean test for BRB rights to BRA returns true' );
+ok( !GetIndependentGroupModificationRights({ branch => 'BRB', for => 'BRC'}), 'Boolean test for BRB rights to BRC returns false' );
+ok( GetIndependentGroupModificationRights({ branch => 'BRC', for => 'BRC' }), 'Boolean test for BRC rights to BRC returns true' );
+ok( !GetIndependentGroupModificationRights({ branch => 'BRC', for => 'BRA'}), 'Boolean test for BRC rights to BRA returns false' );
+ok( !GetIndependentGroupModificationRights({ branch => 'BRC', for => 'BRB'}), 'Boolean test for BRC rights to BRB returns false' );
+
+ModBranch({
+    branchcode     => 'BRC',
+    branchname     => 'BranchC',
+    LIBCATCODE     => 1,
+});
+
+@branches_bra = GetIndependentGroupModificationRights({ branch => 'BRA' });
+is_deeply( \@branches_bra, [ 'BRA', 'BRB', 'BRC' ], 'Library with no group only has rights for its own branch' );
+
+ok( GetIndependentGroupModificationRights({ branch => 'BRA', for => 'BRA' }), 'Boolean test for BRA rights to BRA returns true' );
+ok( GetIndependentGroupModificationRights({ branch => 'BRA', for => 'BRB'}), 'Boolean test for BRA rights to BRB returns true' );
+ok( GetIndependentGroupModificationRights({ branch => 'BRA', for => 'BRC'}), 'Boolean test for BRA rights to BRC returns true' );
+ok( GetIndependentGroupModificationRights({ branch => 'BRB', for => 'BRB' }), 'Boolean test for BRB rights to BRB returns true' );
+ok( GetIndependentGroupModificationRights({ branch => 'BRB', for => 'BRA'}), 'Boolean test for BRB rights to BRA returns true' );
+ok( GetIndependentGroupModificationRights({ branch => 'BRB', for => 'BRC'}), 'Boolean test for BRB rights to BRC returns true' );
+ok( GetIndependentGroupModificationRights({ branch => 'BRC', for => 'BRC' }), 'Boolean test for BRC rights to BRC returns true' );
+ok( GetIndependentGroupModificationRights({ branch => 'BRC', for => 'BRA'}), 'Boolean test for BRC rights to BRA returns true' );
+ok( GetIndependentGroupModificationRights({ branch => 'BRC', for => 'BRA'}), 'Boolean test for BRC rights to BRB returns true' );
+
+$string = GetIndependentGroupModificationRights({ branch => 'BRA', stringify => 1 });
+ok( $string eq q{'BRA','BRB','BRC'}, "String returns correctly" );
+
 # End transaction
 $dbh->rollback;
 
-- 
1.7.2.5