Bug 32030: ERM - Users
authorJonathan Druart <jonathan.druart@bugs.koha-community.org>
Tue, 8 Mar 2022 11:03:14 +0000 (12:03 +0100)
committerTomas Cohen Arazi <tomascohen@theke.io>
Tue, 8 Nov 2022 12:43:36 +0000 (09:43 -0300)
Signed-off-by: Jonathan Field <jonathan.field@ptfs-europe.com>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
Koha/ERM/Agreement.pm
Koha/ERM/Agreement/UserRole.pm [new file with mode: 0644]
Koha/ERM/Agreement/UserRoles.pm [new file with mode: 0644]
Koha/REST/V1/ERM.pm [new file with mode: 0644]
api/v1/swagger/paths/erm_users.yaml [new file with mode: 0644]
api/v1/swagger/swagger.yaml
erm/agreements.pl
koha-tmpl/intranet-tmpl/prog/en/includes/patron-search.inc
koha-tmpl/intranet-tmpl/prog/en/modules/erm/agreements.tt

index bfa6876..2b8789b 100644 (file)
@@ -22,6 +22,7 @@ use Koha::Database;
 use base qw(Koha::Object);
 
 use Koha::ERM::Agreement::Periods;
+use Koha::ERM::Agreement::UserRoles;
 
 =head1 NAME
 
@@ -46,6 +47,19 @@ sub periods {
     return Koha::ERM::Agreement::Periods->_new_from_dbic($periods_rs);
 }
 
+=head3 user_roles
+
+Returns the user roles for this agreement
+
+=cut
+
+sub user_roles {
+    my ( $self ) = @_;
+
+    my $user_roles_rs = $self->_result->erm_agreement_user_roles;
+    return Koha::ERM::Agreement::UserRoles->_new_from_dbic($user_roles_rs);
+}
+
 =head2 Internal methods
 
 =head3 _type
diff --git a/Koha/ERM/Agreement/UserRole.pm b/Koha/ERM/Agreement/UserRole.pm
new file mode 100644 (file)
index 0000000..9e749ac
--- /dev/null
@@ -0,0 +1,58 @@
+package Koha::ERM::Agreement::UserRole;
+
+# 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, see <http://www.gnu.org/licenses>.
+
+use Modern::Perl;
+
+use Koha::Database;
+
+use Koha::Patrons;
+
+use base qw(Koha::Object);
+
+=head1 NAME
+
+Koha::ERM::Agreement::UserRole - Koha Agreement UserRole Object class
+
+=head1 API
+
+=head2 Class Methods
+
+=cut
+
+=head3 patron
+
+Return the patron linked to this user role
+
+=cut
+
+sub patron {
+    my ( $self ) = @_;
+    my $patron_rs = $self->_result->user;
+    return Koha::Patron->_new_from_dbic($patron_rs);
+}
+
+=head2 Internal methods
+
+=head3 _type
+
+=cut
+
+sub _type {
+    return 'ErmAgreementUserRole';
+}
+
+1;
diff --git a/Koha/ERM/Agreement/UserRoles.pm b/Koha/ERM/Agreement/UserRoles.pm
new file mode 100644 (file)
index 0000000..f200f25
--- /dev/null
@@ -0,0 +1,53 @@
+package Koha::ERM::Agreement::UserRoles;
+
+# 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, see <http://www.gnu.org/licenses>.
+
+use Modern::Perl;
+
+
+use Koha::Database;
+
+use Koha::ERM::Agreement::UserRole;
+
+use base qw(Koha::Objects);
+
+=head1 NAME
+
+Koha::ERM::Agreement::UserRoles- Koha Agreement UserRole Object set class
+
+=head1 API
+
+=head2 Class Methods
+
+=cut
+
+=head3 type
+
+=cut
+
+sub _type {
+    return 'ErmAgreementUserRole';
+}
+
+=head3 object_class
+
+=cut
+
+sub object_class {
+    return 'Koha::ERM::Agreement::UserRole';
+}
+
+1;
diff --git a/Koha/REST/V1/ERM.pm b/Koha/REST/V1/ERM.pm
new file mode 100644 (file)
index 0000000..3f98a8e
--- /dev/null
@@ -0,0 +1,58 @@
+package Koha::REST::V1::ERM;
+
+# 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, see <http://www.gnu.org/licenses>.
+
+use Modern::Perl;
+
+use Mojo::Base 'Mojolicious::Controller';
+
+use Koha::Patrons;
+
+use Try::Tiny qw( catch try );
+
+=head1 NAME
+
+Koha::REST::V1::Acquisitions::Funds
+
+=head1 API
+
+=head2 Class methods
+
+=head3 list_users
+
+Return the list of possible ERM' users
+
+=cut
+
+sub list_users {
+    my $c = shift->openapi->valid_input or return;
+
+    return try {
+
+        my $patrons_rs = Koha::Patrons->search->filter_by_have_permission('erm');
+        my $patrons    = $c->objects->search( $patrons_rs );
+
+        return $c->render(
+            status  => 200,
+            openapi => $patrons
+        );
+    }
+    catch {
+        $c->unhandled_exception($_);
+    };
+}
+
+1;
diff --git a/api/v1/swagger/paths/erm_users.yaml b/api/v1/swagger/paths/erm_users.yaml
new file mode 100644 (file)
index 0000000..b06bb21
--- /dev/null
@@ -0,0 +1,47 @@
+---
+/erm/users:
+  get:
+    x-mojo-to: ERM#list_users
+    operationId: listERMUsers
+    description: This resource returns a list of patron allowed to be users of the ERM module
+    summary: List possibe users for ERM
+    tags:
+      - ERM
+    parameters:
+      - $ref: ../parameters.yaml#/match
+      - $ref: ../parameters.yaml#/order_by
+      - $ref: ../parameters.yaml#/page
+      - $ref: ../parameters.yaml#/per_page
+      - $ref: ../parameters.yaml#/q_param
+      - $ref: ../parameters.yaml#/q_body
+      - $ref: ../parameters.yaml#/q_header
+    produces:
+      - application/json
+    responses:
+      "200":
+        description: A list of ERM' users
+        schema:
+          type: array
+          items:
+            $ref: ../definitions.yaml#/patron
+      "403":
+        description: Access forbidden
+        schema:
+          $ref: ../definitions.yaml#/error
+      "500":
+        description: |
+          Internal server error. Possible `error_code` attribute values:
+
+          * `internal_server_error`
+        schema:
+          $ref: ../definitions.yaml#/error
+      "503":
+        description: Under maintenance
+        schema:
+          $ref: ../definitions.yaml#/error
+    x-koha-authorization:
+      permissions:
+        erm: 1
+    x-koha-embed:
+      - extended_attributes
+
index 1bc55f7..62daad3 100644 (file)
@@ -157,6 +157,10 @@ paths:
     $ref: ./paths/config_smtp_servers.yaml#/~1config~1smtp_servers
   "/config/smtp_servers/{smtp_server_id}":
     $ref: "./paths/config_smtp_servers.yaml#/~1config~1smtp_servers~1{smtp_server_id}"
+  /erm/agreements:
+    $ref: ./paths/erm_agreements.yaml#/~1erm~1agreements
+  /erm/users:
+    $ref: ./paths/erm_users.yaml#/~1erm~1users
   /holds:
     $ref: ./paths/holds.yaml#/~1holds
   "/holds/{hold_id}":
index 432b950..88dfa1a 100755 (executable)
@@ -107,6 +107,7 @@ elsif ( $op eq 'add_validate' ) {
         if ( $stored ) {
             if ( $agreement_id ) {
                 $agreement->periods->delete;
+                $agreement->user_roles->delete;
             }
             for my $unique_id ( $input->multi_param('period_unique_id') ) {
                 my $started_on = $input->param( 'started_on_' . $unique_id );
@@ -129,6 +130,20 @@ elsif ( $op eq 'add_validate' ) {
                     }
                 )->store;
             }
+
+            for my $unique_id ( $input->multi_param('user_unique_id') ) {
+                my $user_id = $input->param('user_id_' . $unique_id);
+                next unless $user_id;
+                my $role = $input->param('user_role_' . $unique_id);
+                Koha::ERM::Agreement::UserRole->new(
+                    {
+                        agreement_id => $agreement->agreement_id,
+                        user_id      => $user_id,
+                        role         => $role,
+                    }
+                )->store;
+            }
+
         }
     });
     $op = 'list';
index 3d1de0e..cb97afa 100644 (file)
 [%# - Browse by last name %]
 [%# - The table %]
 [%# Get the following parameters: %]
-[%# - filter: can be 'suggestions_managers', 'orders_managers', 'funds_owners' or 'funds_users' to filter patrons on their permissions %]
+[%# - filter: can be 'suggestions_managers', 'orders_managers', 'funds_owners', 'funds_users' or 'erm_users' to filter patrons on their permissions %]
 [%# - table_id: the ID of the table %]
 [%# open_on_row_click: See patron_search_js %]
 [%# columns: See patron_search_js %]
         <div class="hint">Only staff with superlibrarian or acquisitions permissions (or order_manage permission if granular permissions are enabled) are returned in the search results</div>
     [% ELSIF filter == 'funds_owners' OR filter == 'funds_users' %]
         <div class="hint">Only staff with superlibrarian or acquisitions permissions (or budget_modify permission if granular permissions are enabled) are returned in the search results</div>
+    [% ELSIF filter == 'erm_users' %]
+        <div class="hint">Only staff with superlibrarian or ERM permissions are returned in the search results</div>
     [% END %]
 
     <div class="browse">
             let patron_search_url = '/api/v1/acquisitions/funds/owners';
         [% CASE 'funds_users' %]
             let patron_search_url = '/api/v1/acquisitions/funds/users';
+        [% CASE 'erm_users' %]
+            let patron_search_url = '/api/v1/erm/users';
         [% CASE %]
             let patron_search_url = '/api/v1/patrons';
         [% END %]
index 9505b36..092b61e 100644 (file)
             [% ELSE %]
                 [% PROCESS agreement_period id => 1 %]
             [% END %]
-            <button class="add_new_period" type="button" class="btn btn-primary"><i class="fa fa-plus" aria-hidden="true"></i>Add new period</button>
+            <button class="add_new_period" type="button" class="btn btn-primary"><i class="fa fa-plus" aria-hidden="true"></i> Add new period</button>
+        </fieldset>
+
+[% BLOCK agreement_user %]
+    <fieldset class="agreement_user">
+        <legend>
+            User <span class="user_count">[% id | html %]</span>
+            <a href="#" class="remove_user"><i class="fa fa-trash"></i> Remove this user</a>
+        </legend>
+        <input type="hidden" name="user_unique_id" value="[% id | html %]" />
+
+        <ol>
+            <li>
+                <label for="user" class="required">User: </label>
+                <span class="user">
+                    [% IF u %]
+                        <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% u.user_id| uri %]">
+                            [% INCLUDE 'patron-title.inc' patron = u.patron %]
+                        </a>
+                        <input type="hidden" class="user_id" name="user_id_[% id | html %]" value="[% u.user_id %]" />
+                    [% END %]
+                </span>
+                (<a href="#" class="pick_user" class="btn btn-default">Select user</a>)
+            </li>
+
+            <li>
+                <label>Role: </label>
+                <select class="user_role" name="user_role_[% id | html %]">
+                    <option value=""></option>
+                    [% PROCESS options_for_authorised_values authorised_values => AuthorisedValues.GetAuthValueDropbox( 'ERM_AGREEMENT_USER_ROLES' ), selected_av => u.role %]
+                </select>
+            </li>
+        </ol>
+    </fieldset>
+[% END %]
+
+        <fieldset class="rows">
+            <legend>Users</legend>
+            [% IF agreement.user_roles.count %]
+                [% FOR u IN agreement.user_roles %]
+                    [% PROCESS agreement_user user => u, id => loop.count %]
+                [% END %]
+            [% ELSE %]
+                [% PROCESS agreement_user, id => 1 %]
+            [% END %]
+
+            <button class="add_new_user_block" type="button" class="btn btn-primary"><i class="fa fa-plus" aria-hidden="true"></i> Add new user</button>
         </fieldset>
 
         <fieldset class="action">
     [% INCLUDE 'calendar.inc' %]
     [% INCLUDE 'datatables.inc' %]
     [% INCLUDE 'columns_settings.inc' %]
+    [% INCLUDE 'js-patron-format.inc' %]
     <script>
 
         const agreement_statuses = [% To.json(AuthorisedValues.Get('ERM_AGREEMENT_STATUS')) | $raw %];
         }, {});
 
 
-
+        let current_user_node;
         var columns_settings = [% TablesSettings.GetColumns( 'erm', 'agreements', 'table_agreements', 'json' ) | $raw %];
         $(document).ready(function() {
             var agreements_table_url = '/api/v1/erm/agreements?';
 
             $(".add_new_period").on("click", function(e){
                 e.preventDefault();
-                let new_period_block = $("fieldset.agreement_period:first").clone(1);
-                new_period_block.find("input").val("");
-                $(new_period_block).insertBefore(this);
+                let first_period_block = $("fieldset.agreement_period:first");
+                if ( first_period_block.is(":visible") ) {
+                    let new_period_block = first_period_block.clone(1);
+                    new_period_block.find("input").val("");
+                    $(new_period_block).insertBefore(this);
+                } else {
+                    first_period_block.show();
+                }
                 update_period_count();
             });
             $(".remove_period").on("click", function(e){
                 e.preventDefault();
-                $(this).parent().parent().remove();
+                let fieldset = $(this).parent().parent();
+                if ( $("fieldset.agreement_period").length == 1 ) {
+                    clear_block(fieldset);
+                    fieldset.hide();
+                } else {
+                    fieldset.remove();
+                }
                 update_period_count();
             });
+
+            $(".add_new_user_block").on("click",function(e){
+                e.preventDefault();
+                let first_user_block = $("fieldset.agreement_user:first");
+                if ( first_user_block.is(":visible") ) {
+                    let new_user_block = $("fieldset.agreement_user:first").clone(1);
+                    new_user_block.find("span.user").empty();
+                    clear_block(new_user_block);
+                    $(new_user_block).insertBefore(this);
+                } else {
+                    first_user_block.show();
+                }
+
+                update_user_count();
+            });
+            $(".remove_user").on("click", function(e){
+                e.preventDefault();
+                let fieldset = $(this).parent().parent();
+                if ( $("fieldset.agreement_user").length == 1 ) {
+                    fieldset.find("span.user").empty();
+                    clear_block(fieldset);
+                    fieldset.hide();
+                } else {
+                    fieldset.remove();
+                }
+
+                update_user_count();
+            });
+
+            $(".pick_user").on("click", function(e){
+                e.preventDefault();
+                current_user_node = $(this).closest("fieldset");
+                window.open("/cgi-bin/koha/members/search.pl?columns=cardnumber,name,category,branch,action&selection_type=select&filter=erm_users",
+                    'PatronPopup',
+                    'width=740,height=450,location=yes,toolbar=no,'
+                    + 'scrollbars=yes,resize=yes'
+                );
+            });
+
             update_period_count();
+            update_user_count();
         });
 
+        function clear_block(block){
+            $(block).find('input').val("");
+            $(block).find("select option:first-child").attr("selected", "selected");
+        }
         function update_period_count(){
             $("fieldset.agreement_period").each(function(i, period){
                 let id = i + 1;
-                let remove_period_link = $(this).find(".remove_period");
-                if ( id == 1 ) {
-                    $(remove_period_link).hide();
-                } else {
-                    $(remove_period_link).show();
-                }
                 $(period).find(".period_count").text(id);
                 $(period).find("input[name='period_unique_id']").val(id);
 
                 $(cancellation_deadline_input).flatpickr();
 
                 $(period).find(".notes").attr("name", "notes_" + id);
+                $(period).attr('id', 'agreement_period_' + id);
             });
         }
 
+        function update_user_count(){
+            $("fieldset.agreement_user").each(function(i, user){
+                let id = i + 1;
+                let remove_user_link = $(this).find(".remove_user");
+                $(user).find(".user_count").text(id);
+                $(user).find("input[name='user_unique_id']").val(id);
+
+                $(user).find(".user_id").attr("name", "user_id_" + id);
+                $(user).find(".user_role").attr("name", "user_role_" + id);
+            });
+        }
+
+        function select_user(borrowernumber, patron) {
+            patron['patron_id'] = borrowernumber;
+            let unique_id = $(current_user_node).find("input[name='user_unique_id']").val();
+            let a = '<a href="/cgi-bin/koha/members/moremember.pl?borrowernumber='
+                + borrowernumber + '">' + $patron_to_html(patron) + '</a> '
+                + '<input type="hidden" class="user_id" name="user_id_' + unique_id + '" value="' + borrowernumber + '" />';
+            $(current_user_node).find("span.user").html(a);
+        }
+
+
     </script>
 [% END %]
 [% INCLUDE 'intranet-bottom.inc' %]