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

(-)a/Koha/CirculationRules.pm (-3 / +64 lines)
Lines 37-150 Koha::CirculationRules - Koha CirculationRule Object set class Link Here
37
37
38
=head3 rule_kinds
38
=head3 rule_kinds
39
39
40
This structure describes the possible rules that may be set, and what scopes they can be set at.
40
This structure describes the possible rules that may be set, what scopes they can be set at and any
41
default values.
41
42
42
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.
43
Any attempt to set a rule with a nonsensical scope (for instance, setting the C<patron_maxissueqty>
44
for a branchcode and itemtype), is an error.
45
46
These default values correspond to what would be stored in the database if nothing was changed on
47
the old circulation rules editor; this is a combination of the default values in the template and
48
the issuingrules table.
43
49
44
=cut
50
=cut
45
51
46
our $RULE_KINDS = {
52
our $RULE_KINDS = {
47
    refund => {
53
    refund => {
48
        scope => [ 'branchcode' ],
54
        scope => [ 'branchcode' ],
55
        default_value => 1,
49
    },
56
    },
50
57
51
    patron_maxissueqty => {
58
    patron_maxissueqty => {
52
        scope => [ 'branchcode', 'categorycode' ],
59
        scope => [ 'branchcode', 'categorycode' ],
60
        default_value => "",
53
    },
61
    },
54
    patron_maxonsiteissueqty => {
62
    patron_maxonsiteissueqty => {
55
        scope => [ 'branchcode', 'categorycode' ],
63
        scope => [ 'branchcode', 'categorycode' ],
64
        default_value => "",
56
    },
65
    },
57
    max_holds => {
66
    max_holds => {
58
        scope => [ 'branchcode', 'categorycode' ],
67
        scope => [ 'branchcode', 'categorycode' ],
68
        default_value => "",
59
    },
69
    },
60
70
61
    holdallowed => {
71
    holdallowed => {
62
        scope => [ 'branchcode', 'itemtype' ],
72
        scope => [ 'branchcode', 'itemtype' ],
73
        default_value => 2,
63
    },
74
    },
64
    hold_fulfillment_policy => {
75
    hold_fulfillment_policy => {
65
        scope => [ 'branchcode', 'itemtype' ],
76
        scope => [ 'branchcode', 'itemtype' ],
77
        default_value => 'any',
66
    },
78
    },
67
    returnbranch => {
79
    returnbranch => {
68
        scope => [ 'branchcode', 'itemtype' ],
80
        scope => [ 'branchcode', 'itemtype' ],
81
        default_value => 'homebranch',
69
    },
82
    },
70
83
71
    article_requests => {
84
    article_requests => {
72
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
85
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
86
        default_value => 'no',
73
    },
87
    },
74
    auto_renew => {
88
    auto_renew => {
75
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
89
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
90
        default_value => 0,
76
    },
91
    },
77
    cap_fine_to_replacement_price => {
92
    cap_fine_to_replacement_price => {
78
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
93
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
94
        default_value => 0,
79
    },
95
    },
80
    chargeperiod => {
96
    chargeperiod => {
81
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
97
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
98
        default_value => 0,
82
    },
99
    },
83
    chargeperiod_charge_at => {
100
    chargeperiod_charge_at => {
84
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
101
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
102
        default_value => 0,
85
    },
103
    },
86
    fine => {
104
    fine => {
87
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
105
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
106
        default_value => 0,
88
    },
107
    },
89
    finedays => {
108
    finedays => {
90
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
109
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
110
        default_value => 0,
91
    },
111
    },
92
    firstremind => {
112
    firstremind => {
93
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
113
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
114
        default_value => 0,
94
    },
115
    },
95
    hardduedate => {
116
    hardduedate => {
96
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
117
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
118
        default_value => "",
97
    },
119
    },
98
    hardduedatecompare => {
120
    hardduedatecompare => {
99
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
121
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
122
        default_value => -1,
100
    },
123
    },
101
    holds_per_record => {
124
    holds_per_record => {
102
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
125
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
126
        default_value => 1,
103
    },
127
    },
104
    issuelength => {
128
    issuelength => {
105
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
129
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
130
        default_value => 0,
106
    },
131
    },
107
    lengthunit => {
132
    lengthunit => {
108
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
133
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
134
        default_value => 'days',
109
    },
135
    },
110
    maxissueqty => {
136
    maxissueqty => {
111
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
137
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
138
        default_value => "",
112
    },
139
    },
113
    maxonsiteissueqty => {
140
    maxonsiteissueqty => {
114
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
141
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
142
        default_value => "",
115
    },
143
    },
116
    maxsuspensiondays => {
144
    maxsuspensiondays => {
117
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
145
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
146
        default_value => "",
118
    },
147
    },
119
    no_auto_renewal_after => {
148
    no_auto_renewal_after => {
120
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
149
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
150
        default_value => "",
121
    },
151
    },
122
    no_auto_renewal_after_hard_limit => {
152
    no_auto_renewal_after_hard_limit => {
123
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
153
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
154
        default_value => "",
124
    },
155
    },
125
    norenewalbefore => {
156
    norenewalbefore => {
126
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
157
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
158
        default_value => "",
127
    },
159
    },
128
    onshelfholds => {
160
    onshelfholds => {
129
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
161
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
162
        default_value => 0,
130
    },
163
    },
131
    opacitemholds => {
164
    opacitemholds => {
132
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
165
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
166
        default_value => "N",
133
    },
167
    },
134
    overduefinescap => {
168
    overduefinescap => {
135
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
169
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
170
        default_value => "",
136
    },
171
    },
137
    renewalperiod => {
172
    renewalperiod => {
138
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
173
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
174
        default_value => 0,
139
    },
175
    },
140
    renewalsallowed => {
176
    renewalsallowed => {
141
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
177
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
178
        default_value => 0,
142
    },
179
    },
143
    rentaldiscount => {
180
    rentaldiscount => {
144
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
181
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
182
        default_value => 0,
145
    },
183
    },
146
    reservesallowed => {
184
    reservesallowed => {
147
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
185
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
186
        default_value => 0,
148
    },
187
    },
149
    # Not included (deprecated?):
188
    # Not included (deprecated?):
150
    #   * accountsent
189
    #   * accountsent
Lines 227-232 sub get_effective_rules { Link Here
227
266
228
=head3 set_rule
267
=head3 set_rule
229
268
269
=over 4
270
Koha::CirculationRules->set_rule( {
271
    branchcode => $branchcode,
272
    [ categorycode => $categorycode, ]
273
    [ itemtype => $itemtype, ]
274
    rule_name => $rule_name,
275
    rule_value => $rule_value,
276
    [ allow_null_out_of_scope => 1, ]
277
} )
278
=back
279
280
Sets a single circulation rule.
281
282
It is an error to specify a C<categorycode> or C<itemtype> unless the given C<rule_name> can be set
283
at that scope; i.e., you cannot set a C<patron_maxissueqty> for an itemtype.
284
285
If C<allow_null_out_of_scope> is passed, and any excess C<categorycode> or C<itemtype> is set to C<undef>,
286
they are ignored.
287
230
=cut
288
=cut
231
289
232
sub set_rule {
290
sub set_rule {
Lines 248-254 sub set_rule { Link Here
248
                unless exists $params->{$scope_level};
306
                unless exists $params->{$scope_level};
249
        } else {
307
        } else {
250
            croak "set_rule cannot set '$params->{rule_name}' for a '$scope_level'!"
308
            croak "set_rule cannot set '$params->{rule_name}' for a '$scope_level'!"
251
                if exists $params->{$scope_level};
309
                unless !exists $params->{$scope_level} || (
310
                    $params->{allow_null_out_of_scope} &&
311
                    !defined $params->{$scope_level}
312
                );
252
        }
313
        }
253
    }
314
    }
254
315
(-)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 32-35 sub get_kinds { Link Here
32
    );
37
    );
33
}
38
}
34
39
40
sub get_rules {
41
    my $c = shift->openapi->valid_input or return;
42
43
    return $c->render(
44
        status => 200,
45
        openapi => [ Koha::CirculationRules->search ],
46
    );
47
}
48
49
sub save_rules {
50
    my $c = shift->openapi->valid_input or return;
51
52
    my $schema = Koha::Database->new->schema;
53
54
    my $uid = $c->stash( 'koha.user' )->userid;
55
    my $restricted_to_library = $uid && haspermission( $uid, { parameters => 'manage_circ_rules_restricted' }, { no_inherit => 1 } ) ? $c->stash( 'koha.user' )->branchcode : "";
56
57
    return try {
58
        my $rules = $c->req->json;
59
60
        $schema->storage->txn_do( sub {
61
            foreach my $rule ( @$rules ) {
62
                if ( $restricted_to_library && ( !$rule->{branchcode} || $rule->{branchcode} ne $restricted_to_library ) ) {
63
                    Koha::Exceptions::Authorization::Restricted->throw(
64
                        error => 'User can only modify settings for their branch.'
65
                    );
66
                }
67
68
                Koha::CirculationRules->set_rule( { %$rule, allow_null_out_of_scope => 1 } );
69
            }
70
        } );
71
72
        return $c->render( status => 200, openapi => "" );
73
    }
74
    catch {
75
        if ( $_->isa('Koha::Exceptions::Authorization::Restricted') ) {
76
            return $c->render( status  => 403,
77
                               openapi => { error => $_->message } );
78
        } else {
79
            warn $_;
80
81
            return $c->render( status => 500,
82
                openapi => { error => "Something went wrong, check the logs."} );
83
        }
84
    };
85
86
}
87
35
1;
88
1;
(-)a/api/v1/swagger/definitions.json (+3 lines)
Lines 1-4 Link Here
1
{
1
{
2
  "circ-rule": {
3
    "$ref": "definitions/circ-rule.json"
4
  },
2
  "circ-rule-kind": {
5
  "circ-rule-kind": {
3
    "$ref": "definitions/circ-rule-kind.json"
6
    "$ref": "definitions/circ-rule-kind.json"
4
  },
7
  },
(-)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 5-10 Link Here
5
  "/acquisitions/vendors/{vendor_id}": {
5
  "/acquisitions/vendors/{vendor_id}": {
6
    "$ref": "paths/acquisitions_vendors.json#/~1acquisitions~1vendors~1{vendor_id}"
6
    "$ref": "paths/acquisitions_vendors.json#/~1acquisitions~1vendors~1{vendor_id}"
7
  },
7
  },
8
  "/circulation-rules": {
9
    "$ref": "paths/circulation-rules.json#/~1circulation-rules"
10
  },
8
  "/circulation-rules/kinds": {
11
  "/circulation-rules/kinds": {
9
    "$ref": "paths/circulation-rules.json#/~1circulation-rules~1kinds"
12
    "$ref": "paths/circulation-rules.json#/~1circulation-rules~1kinds"
10
  },
13
  },
(-)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 7-12 Link Here
7
    "type": "integer",
7
    "type": "integer",
8
    "description": "internally assigned user identifier"
8
    "description": "internally assigned user identifier"
9
  },
9
  },
10
  "branchcode": {
11
    "type": "string",
12
    "description": "library assigned branch identifier"
13
  },
10
  "cardnumber": {
14
  "cardnumber": {
11
    "type": ["string", "null"],
15
    "type": ["string", "null"],
12
    "description": "library assigned user identifier"
16
    "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