View | Details | Raw Unified | Return to bug 15522
Collapse All | Expand All

(-)a/Koha/CirculationRules.pm (-3 / +64 lines)
Lines 39-155 Koha::CirculationRules - Koha CirculationRule Object set class Link Here
39
39
40
=head3 rule_kinds
40
=head3 rule_kinds
41
41
42
This structure describes the possible rules that may be set, and what scopes they can be set at.
42
This structure describes the possible rules that may be set, what scopes they can be set at and any
43
default values.
43
44
44
Any attempt to set a rule with a nonsensical scope (for instance, setting the C<patron_maxissueqty> for a branchcode and itemtype), is an error.
45
Any attempt to set a rule with a nonsensical scope (for instance, setting the C<patron_maxissueqty>
46
for a branchcode and itemtype), is an error.
47
48
These default values correspond to what would be stored in the database if nothing was changed on
49
the old circulation rules editor; this is a combination of the default values in the template and
50
the issuingrules table.
45
51
46
=cut
52
=cut
47
53
48
our $RULE_KINDS = {
54
our $RULE_KINDS = {
49
    refund => {
55
    refund => {
50
        scope => [ 'branchcode' ],
56
        scope => [ 'branchcode' ],
57
        default_value => 1,
51
    },
58
    },
52
59
53
    patron_maxissueqty => {
60
    patron_maxissueqty => {
54
        scope => [ 'branchcode', 'categorycode' ],
61
        scope => [ 'branchcode', 'categorycode' ],
62
        default_value => "",
55
    },
63
    },
56
    patron_maxonsiteissueqty => {
64
    patron_maxonsiteissueqty => {
57
        scope => [ 'branchcode', 'categorycode' ],
65
        scope => [ 'branchcode', 'categorycode' ],
66
        default_value => "",
58
    },
67
    },
59
    max_holds => {
68
    max_holds => {
60
        scope => [ 'branchcode', 'categorycode' ],
69
        scope => [ 'branchcode', 'categorycode' ],
70
        default_value => "",
61
    },
71
    },
62
72
63
    holdallowed => {
73
    holdallowed => {
64
        scope => [ 'branchcode', 'itemtype' ],
74
        scope => [ 'branchcode', 'itemtype' ],
75
        default_value => 2,
65
    },
76
    },
66
    hold_fulfillment_policy => {
77
    hold_fulfillment_policy => {
67
        scope => [ 'branchcode', 'itemtype' ],
78
        scope => [ 'branchcode', 'itemtype' ],
79
        default_value => 'any',
68
    },
80
    },
69
    returnbranch => {
81
    returnbranch => {
70
        scope => [ 'branchcode', 'itemtype' ],
82
        scope => [ 'branchcode', 'itemtype' ],
83
        default_value => 'homebranch',
71
    },
84
    },
72
85
73
    article_requests => {
86
    article_requests => {
74
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
87
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
88
        default_value => 'no',
75
    },
89
    },
76
    auto_renew => {
90
    auto_renew => {
77
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
91
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
92
        default_value => 0,
78
    },
93
    },
79
    cap_fine_to_replacement_price => {
94
    cap_fine_to_replacement_price => {
80
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
95
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
96
        default_value => 0,
81
    },
97
    },
82
    chargeperiod => {
98
    chargeperiod => {
83
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
99
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
100
        default_value => 0,
84
    },
101
    },
85
    chargeperiod_charge_at => {
102
    chargeperiod_charge_at => {
86
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
103
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
104
        default_value => 0,
87
    },
105
    },
88
    fine => {
106
    fine => {
89
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
107
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
108
        default_value => 0,
90
    },
109
    },
91
    finedays => {
110
    finedays => {
92
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
111
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
112
        default_value => 0,
93
    },
113
    },
94
    firstremind => {
114
    firstremind => {
95
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
115
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
116
        default_value => 0,
96
    },
117
    },
97
    hardduedate => {
118
    hardduedate => {
98
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
119
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
120
        default_value => "",
99
    },
121
    },
100
    hardduedatecompare => {
122
    hardduedatecompare => {
101
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
123
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
124
        default_value => -1,
102
    },
125
    },
103
    holds_per_day => {
126
    holds_per_day => {
104
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
127
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
105
    },
128
    },
106
    holds_per_record => {
129
    holds_per_record => {
107
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
130
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
131
        default_value => 1,
108
    },
132
    },
109
    issuelength => {
133
    issuelength => {
110
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
134
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
135
        default_value => 0,
111
    },
136
    },
112
    lengthunit => {
137
    lengthunit => {
113
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
138
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
139
        default_value => 'days',
114
    },
140
    },
115
    maxissueqty => {
141
    maxissueqty => {
116
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
142
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
143
        default_value => "",
117
    },
144
    },
118
    maxonsiteissueqty => {
145
    maxonsiteissueqty => {
119
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
146
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
147
        default_value => "",
120
    },
148
    },
121
    maxsuspensiondays => {
149
    maxsuspensiondays => {
122
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
150
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
151
        default_value => "",
123
    },
152
    },
124
    no_auto_renewal_after => {
153
    no_auto_renewal_after => {
125
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
154
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
155
        default_value => "",
126
    },
156
    },
127
    no_auto_renewal_after_hard_limit => {
157
    no_auto_renewal_after_hard_limit => {
128
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
158
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
159
        default_value => "",
129
    },
160
    },
130
    norenewalbefore => {
161
    norenewalbefore => {
131
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
162
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
163
        default_value => "",
132
    },
164
    },
133
    onshelfholds => {
165
    onshelfholds => {
134
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
166
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
167
        default_value => 0,
135
    },
168
    },
136
    opacitemholds => {
169
    opacitemholds => {
137
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
170
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
171
        default_value => "N",
138
    },
172
    },
139
    overduefinescap => {
173
    overduefinescap => {
140
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
174
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
175
        default_value => "",
141
    },
176
    },
142
    renewalperiod => {
177
    renewalperiod => {
143
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
178
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
179
        default_value => 0,
144
    },
180
    },
145
    renewalsallowed => {
181
    renewalsallowed => {
146
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
182
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
183
        default_value => 0,
147
    },
184
    },
148
    rentaldiscount => {
185
    rentaldiscount => {
149
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
186
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
187
        default_value => 0,
150
    },
188
    },
151
    reservesallowed => {
189
    reservesallowed => {
152
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
190
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
191
        default_value => 0,
153
    },
192
    },
154
    suspension_chargeperiod => {
193
    suspension_chargeperiod => {
155
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
194
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
Lines 243-248 sub get_effective_rules { Link Here
243
282
244
=head3 set_rule
283
=head3 set_rule
245
284
285
=over 4
286
Koha::CirculationRules->set_rule( {
287
    branchcode => $branchcode,
288
    [ categorycode => $categorycode, ]
289
    [ itemtype => $itemtype, ]
290
    rule_name => $rule_name,
291
    rule_value => $rule_value,
292
    [ allow_null_out_of_scope => 1, ]
293
} )
294
=back
295
296
Sets a single circulation rule.
297
298
It is an error to specify a C<categorycode> or C<itemtype> unless the given C<rule_name> can be set
299
at that scope; i.e., you cannot set a C<patron_maxissueqty> for an itemtype.
300
301
If C<allow_null_out_of_scope> is passed, and any excess C<categorycode> or C<itemtype> is set to C<undef>,
302
they are ignored.
303
246
=cut
304
=cut
247
305
248
sub set_rule {
306
sub set_rule {
Lines 266-272 sub set_rule { Link Here
266
                unless exists $params->{$scope_level};
324
                unless exists $params->{$scope_level};
267
        } else {
325
        } else {
268
            croak "set_rule cannot set '$params->{rule_name}' for a '$scope_level'!"
326
            croak "set_rule cannot set '$params->{rule_name}' for a '$scope_level'!"
269
                if exists $params->{$scope_level};
327
                unless !exists $params->{$scope_level} || (
328
                    $params->{allow_null_out_of_scope} &&
329
                    !defined $params->{$scope_level}
330
                );
270
        }
331
        }
271
    }
332
    }
272
333
(-)a/Koha/Exceptions/Authorization.pm (+3 lines)
Lines 12-17 use Exception::Class ( Link Here
12
        description => 'Unauthorized',
12
        description => 'Unauthorized',
13
        fields => ['required_permissions']
13
        fields => ['required_permissions']
14
    },
14
    },
15
    'Koha::Exceptions::Authorization::Restricted' => {
16
        description => 'User can only modify settings for their branch'
17
    },
15
18
16
);
19
);
17
20
(-)a/Koha/REST/V1/CirculationRules.pm (+53 lines)
Lines 19-25 use Modern::Perl; Link Here
19
19
20
use Mojo::Base 'Mojolicious::Controller';
20
use Mojo::Base 'Mojolicious::Controller';
21
21
22
use C4::Auth;
23
use C4::Context;
24
22
use Koha::CirculationRules;
25
use Koha::CirculationRules;
26
use Koha::Database;
27
use Koha::Exceptions::Authorization;
23
28
24
use Try::Tiny;
29
use Try::Tiny;
25
30
Lines 42-45 sub get_kinds { Link Here
42
    );
47
    );
43
}
48
}
44
49
50
sub get_rules {
51
    my $c = shift->openapi->valid_input or return;
52
53
    return $c->render(
54
        status => 200,
55
        openapi => [ Koha::CirculationRules->search ],
56
    );
57
}
58
59
sub save_rules {
60
    my $c = shift->openapi->valid_input or return;
61
62
    my $schema = Koha::Database->new->schema;
63
64
    my $uid = $c->stash( 'koha.user' )->userid;
65
    my $restricted_to_library = $uid && haspermission( $uid, { parameters => 'manage_circ_rules_restricted' }, { no_inherit => 1 } ) ? $c->stash( 'koha.user' )->branchcode : "";
66
67
    return try {
68
        my $rules = $c->req->json;
69
70
        $schema->storage->txn_do( sub {
71
            foreach my $rule ( @$rules ) {
72
                if ( $restricted_to_library && ( !$rule->{branchcode} || $rule->{branchcode} ne $restricted_to_library ) ) {
73
                    Koha::Exceptions::Authorization::Restricted->throw(
74
                        error => 'User can only modify settings for their branch.'
75
                    );
76
                }
77
78
                Koha::CirculationRules->set_rule( { %$rule, allow_null_out_of_scope => 1 } );
79
            }
80
        } );
81
82
        return $c->render( status => 200, openapi => "" );
83
    }
84
    catch {
85
        if ( $_->isa('Koha::Exceptions::Authorization::Restricted') ) {
86
            return $c->render( status  => 403,
87
                               openapi => { error => $_->message } );
88
        } else {
89
            warn $_;
90
91
            return $c->render( status => 500,
92
                openapi => { error => "Something went wrong, check the logs."} );
93
        }
94
    };
95
96
}
97
45
1;
98
1;
(-)a/api/v1/swagger/definitions.json (+3 lines)
Lines 5-10 Link Here
5
  "basket": {
5
  "basket": {
6
    "$ref": "definitions/basket.json"
6
    "$ref": "definitions/basket.json"
7
  },
7
  },
8
  "circ-rule": {
9
    "$ref": "definitions/circ-rule.json"
10
  },
8
  "circ-rule-kind": {
11
  "circ-rule-kind": {
9
    "$ref": "definitions/circ-rule-kind.json"
12
    "$ref": "definitions/circ-rule-kind.json"
10
  },
13
  },
(-)a/api/v1/swagger/definitions/circ-rule-kind.json (+4 lines)
Lines 8-13 Link Here
8
        "type": "string",
8
        "type": "string",
9
        "enum": [ "branchcode", "categorycode", "itemtype" ]
9
        "enum": [ "branchcode", "categorycode", "itemtype" ]
10
      }
10
      }
11
    },
12
    "default_value": {
13
      "description": "effective default if no rules are set",
14
      "type": [ "string", "null" ]
11
    }
15
    }
12
  },
16
  },
13
  "additionalProperties": false,
17
  "additionalProperties": false,
(-)a/api/v1/swagger/definitions/circ-rule.json (+31 lines)
Line 0 Link Here
1
{
2
  "type": "object",
3
  "properties": {
4
    "id": {
5
      "description": "internal identifier of this rule",
6
      "type": "integer"
7
    },
8
    "branchcode": {
9
      "type": [ "string", "null" ],
10
      "description": "branch that this rule applies to"
11
    },
12
    "categorycode": {
13
      "type": [ "string", "null" ],
14
      "description": "category that this rule applies to"
15
    },
16
    "itemtype": {
17
      "type": [ "string", "null" ],
18
      "description": "itemtype that this rule applies to"
19
    },
20
    "rule_name": {
21
      "type": "string",
22
      "description": "name of the kind of this rule"
23
    },
24
    "rule_value": {
25
      "type": [ "string", "null" ],
26
      "description": "value of the rule"
27
    }
28
  },
29
  "additionalProperties": false,
30
  "required": [ "branchcode", "rule_name", "rule_value" ]
31
}
(-)a/api/v1/swagger/paths.json (+3 lines)
Lines 26-31 Link Here
26
  "/checkouts/{checkout_id}/renewal": {
26
  "/checkouts/{checkout_id}/renewal": {
27
    "$ref": "paths/checkouts.json#/~1checkouts~1{checkout_id}~1renewal"
27
    "$ref": "paths/checkouts.json#/~1checkouts~1{checkout_id}~1renewal"
28
  },
28
  },
29
  "/circulation-rules": {
30
    "$ref": "paths/circulation-rules.json#/~1circulation-rules"
31
  },
29
  "/circulation-rules/kinds": {
32
  "/circulation-rules/kinds": {
30
    "$ref": "paths/circulation-rules.json#/~1circulation-rules~1kinds"
33
    "$ref": "paths/circulation-rules.json#/~1circulation-rules~1kinds"
31
  },
34
  },
(-)a/api/v1/swagger/paths/circulation-rules.json (-1 / +85 lines)
Lines 1-9 Link Here
1
{
1
{
2
  "/circulation-rules": {
3
    "get": {
4
      "x-mojo-to": "CirculationRules#get_rules",
5
      "operationId": "getCirculationRules",
6
      "tags": ["circulation-rules"],
7
      "produces": [
8
        "application/json"
9
      ],
10
      "responses": {
11
        "200": {
12
          "description": "A list of defined rules",
13
          "schema": {
14
            "type": "array",
15
            "items": {
16
              "$ref": "../definitions.json#/circ-rule"
17
            }
18
          }
19
        },
20
        "403": {
21
          "description": "Access forbidden",
22
          "schema": {
23
            "$ref": "../definitions.json#/error"
24
          }
25
        },
26
        "500": {
27
          "description": "Internal error",
28
          "schema": {
29
            "$ref": "../definitions.json#/error"
30
          }
31
        }
32
      }
33
    },
34
    "post": {
35
      "x-mojo-to": "CirculationRules#save_rules",
36
      "operationId": "saveCirculationRules",
37
      "tags": ["circulation-rules"],
38
      "produces": [
39
        "application/json"
40
      ],
41
      "parameters": [{
42
        "name": "body",
43
        "in": "body",
44
        "description": "A JSON object containing rules to set; null for a rule_value means to delete the rule",
45
        "required": true,
46
        "schema": {
47
          "type": "array",
48
          "items": {
49
            "$ref": "../definitions.json#/circ-rule"
50
          }
51
        }
52
      }],
53
      "responses": {
54
        "200": {
55
          "description": "Rules saved",
56
          "schema": {
57
            "type": "string"
58
          }
59
        },
60
        "401": {
61
          "description": "Authentication required",
62
          "schema": {
63
            "$ref": "../definitions.json#/error"
64
          }
65
        },
66
        "403": {
67
          "description": "Access forbidden",
68
          "schema": {
69
            "$ref": "../definitions.json#/error"
70
          }
71
        },
72
        "500": {
73
          "description": "Internal error",
74
          "schema": {
75
            "$ref": "../definitions.json#/error"
76
          }
77
        }
78
      },
79
      "x-koha-authorization": {
80
        "permissions": {
81
          "parameters": "manage_circ_rules"
82
        }
83
      }
84
    }
85
  },
2
  "/circulation-rules/kinds": {
86
  "/circulation-rules/kinds": {
3
    "get": {
87
    "get": {
4
      "x-mojo-to": "CirculationRules#get_kinds",
88
      "x-mojo-to": "CirculationRules#get_kinds",
5
      "operationId": "getCirculationRuleKinds",
89
      "operationId": "getCirculationRuleKinds",
6
      "tags": ["cities"],
90
      "tags": ["circulation-rules"],
7
      "produces": [
91
      "produces": [
8
        "application/json"
92
        "application/json"
9
      ],
93
      ],
(-)a/api/v1/swagger/x-primitives.json (+4 lines)
Lines 13-18 Link Here
13
    "maxLength": 10,
13
    "maxLength": 10,
14
    "minLength": 1
14
    "minLength": 1
15
  },
15
  },
16
  "branchcode": {
17
    "type": "string",
18
    "description": "library assigned branch identifier"
19
  },
16
  "cardnumber": {
20
  "cardnumber": {
17
    "type": ["string", "null"],
21
    "type": ["string", "null"],
18
    "description": "library assigned user identifier"
22
    "description": "library assigned user identifier"
(-)a/t/db_dependent/api/v1/circ_rules.t (-1 / +268 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/env perl
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it under the
6
# terms of the GNU General Public License as published by the Free Software
7
# Foundation; either version 3 of the License, or (at your option) any later
8
# version.
9
#
10
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License along
15
# with Koha; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18
use Modern::Perl;
19
20
use Test::More tests => 5;
21
use Test::Deep qw( cmp_deeply set superhashof );
22
use Test::Mojo;
23
use Test::Warn;
24
25
use t::lib::TestBuilder;
26
use t::lib::Mocks;
27
28
use C4::Auth;
29
use Koha::Acquisition::Booksellers;
30
use Koha::CirculationRules;
31
use Koha::Database;
32
33
my $schema  = Koha::Database->new->schema;
34
my $builder = t::lib::TestBuilder->new;
35
36
# FIXME: sessionStorage defaults to mysql, but it seems to break transaction handling
37
# this affects the other REST api tests
38
t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' );
39
40
my $remote_address = '127.0.0.1';
41
my $t              = Test::Mojo->new('Koha::REST::V1');
42
43
sub tx_maker {
44
    my $args = shift;
45
46
    my ( $borrowernumber, $session_id ) = create_user_and_session( $args );
47
48
    return sub {
49
        my $tx = shift->ua->build_tx( @_ );
50
        $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
51
        $tx->req->env( { REMOTE_ADDR => $remote_address } );
52
53
        return $tx;
54
    }
55
}
56
57
subtest 'rule_kinds' => sub {
58
    plan tests => 3;
59
60
    my $tx_maker = tx_maker( { permissions => 'authorized' } );
61
62
    my $tx = $tx_maker->( $t, GET => '/api/v1/circulation-rules/kinds' );
63
    $t->request_ok($tx)
64
      ->status_is(200)
65
      ->json_is( Koha::CirculationRules->rule_kinds );
66
};
67
68
subtest 'get_rules' => sub {
69
    plan tests => 6;
70
71
    $schema->storage->txn_begin;
72
73
    Koha::CirculationRules->search->delete;
74
75
    my $tx_maker = tx_maker( { permissions => 'authorized' } );
76
77
    my $tx = $tx_maker->( $t, GET => '/api/v1/circulation-rules' );
78
    $t->request_ok($tx)
79
      ->status_is(200)
80
      ->json_is( [] );
81
82
    my $branchcode = $builder->build({ source => 'Branch' })->{'branchcode'};
83
    my @rules = (
84
        {
85
            branchcode => undef,
86
            categorycode => undef,
87
            itemtype => undef,
88
            rule_name => 'maxissueqty',
89
            rule_value => 10,
90
        },
91
        {
92
            branchcode => $branchcode,
93
            categorycode => undef,
94
            itemtype => undef,
95
            rule_name => 'maxissueqty',
96
            rule_value => 20,
97
        },
98
    );
99
100
    Koha::CirculationRules->set_rule( $_ ) for ( @rules );
101
102
    $tx = $tx_maker->( $t, GET => '/api/v1/circulation-rules' );
103
    $t->request_ok($tx)
104
      ->status_is(200);
105
    cmp_deeply( $tx->res->json, set( map { superhashof( $_ ) } @rules ), 'rules retrieved correctly' );
106
107
    $schema->storage->txn_rollback;
108
};
109
110
subtest 'set_rules | unauthorized' => sub {
111
    plan tests => 2;
112
113
    $schema->storage->txn_begin;
114
115
    Koha::CirculationRules->search->delete;
116
117
    my $tx_maker = tx_maker( { permissions => 'none' } );
118
119
    my $rule = {
120
        branchcode => undef,
121
        categorycode => undef,
122
        itemtype => undef,
123
        rule_name => 'maxissueqty',
124
        rule_value => 10,
125
    };
126
127
    my $tx = $tx_maker->( $t, POST => '/api/v1/circulation-rules', json => $rule );
128
    $t->request_ok($tx)
129
      ->status_is(403);
130
131
    $schema->storage->txn_rollback;
132
};
133
134
subtest 'set_rules' => sub {
135
    plan tests => 3;
136
137
    $schema->storage->txn_begin;
138
139
    Koha::CirculationRules->search->delete;
140
141
    my $tx_maker = tx_maker( { permissions => 'authorized' } );
142
143
    my @rules = (
144
        {
145
            branchcode => undef,
146
            categorycode => undef,
147
            itemtype => undef,
148
            rule_name => 'maxissueqty',
149
            rule_value => 10,
150
        },
151
    );
152
153
    my $tx = $tx_maker->( $t, POST => '/api/v1/circulation-rules', json => \@rules );
154
    $t->request_ok($tx)
155
      ->status_is(200);
156
157
    my $stored_rule = Koha::CirculationRules->search( {
158
        branchcode => undef,
159
        categorycode => undef,
160
        itemtype => undef,
161
    } )->next;
162
163
    cmp_deeply(
164
        $stored_rule && $stored_rule->unblessed,
165
        superhashof( $rules[0] ),
166
        'rule stored correctly'
167
    );
168
169
    $schema->storage->txn_rollback;
170
};
171
172
subtest 'set_rules | restricted' => sub {
173
    plan tests => 6;
174
175
    $schema->storage->txn_begin;
176
177
    Koha::CirculationRules->search->delete;
178
179
    my $branchcode = $builder->build({ source => 'Branch' })->{'branchcode'};
180
    my $other_branchcode = $builder->build({ source => 'Branch' })->{'branchcode'};
181
    my $tx_maker = tx_maker( { permissions => 'authorized_restricted', branchcode => $branchcode } );
182
183
    my @allowed_rules = (
184
        {
185
            branchcode => $branchcode,
186
            categorycode => undef,
187
            itemtype => undef,
188
            rule_name => 'maxissueqty',
189
            rule_value => 10,
190
        },
191
    );
192
193
    my @forbidden_rules = (
194
        {
195
            branchcode => undef,
196
            categorycode => undef,
197
            itemtype => undef,
198
            rule_name => 'maxissueqty',
199
            rule_value => 10,
200
        },
201
        {
202
            branchcode => $other_branchcode,
203
            categorycode => undef,
204
            itemtype => undef,
205
            rule_name => 'maxissueqty',
206
            rule_value => 20,
207
        },
208
    );
209
210
    for my $rule ( @allowed_rules ) {
211
        my $tx = $tx_maker->( $t, POST => '/api/v1/circulation-rules', json => [ $rule ] );
212
        $t->request_ok($tx)
213
          ->status_is(200);
214
    }
215
216
    for my $rule ( @forbidden_rules ) {
217
        my $tx = $tx_maker->( $t, POST => '/api/v1/circulation-rules', json => [ $rule ] );
218
        $t->request_ok($tx)
219
          ->status_is(403);
220
    }
221
222
    $schema->storage->txn_rollback;
223
};
224
225
sub create_user_and_session {
226
227
    my $args = shift;
228
    my $flags = {
229
        none => 0,
230
        # catalogue and parameters permissions
231
        authorized => (1 << 2) | (1 << 3),
232
        # Just catalogue
233
        authorized_restricted => (1 << 2),
234
    }->{ $args->{permissions} || ( $args->{authorized} ? 'authorized' : 'none' ) };
235
236
    my %branch_flags;
237
    $branch_flags{branchcode} = $args->{branchcode} if ( $args->{branchcode} );
238
239
    # my $flags = ( $args->{authorized} ) ? $args->{authorized} : 0;
240
    my $dbh = C4::Context->dbh;
241
242
    my $user = $builder->build(
243
        {   source => 'Borrower',
244
            value  => { flags => $flags, %branch_flags }
245
        }
246
    );
247
248
    # Create a session for the authorized user
249
    my $session = C4::Auth::get_session('');
250
    $session->param( 'number',   $user->{borrowernumber} );
251
    $session->param( 'id',       $user->{userid} );
252
    $session->param( 'ip',       '127.0.0.1' );
253
    $session->param( 'lasttime', time() );
254
    $session->flush;
255
256
    if ( $args->{permissions} eq 'authorized_restricted' ) {
257
        $dbh->do(
258
            q{
259
            INSERT INTO user_permissions (borrowernumber,module_bit,code)
260
            VALUES (?,3,'manage_circ_rules_restricted'), (?,3,'manage_circ_rules')},
261
            undef, $user->{borrowernumber}, $user->{borrowernumber}
262
        );
263
    }
264
265
    return ( $user->{borrowernumber}, $session->id );
266
}
267
268
1;

Return to bug 15522