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

(-)a/Koha/CirculationRules.pm (-3 / +64 lines)
Lines 39-158 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
    daysmode => {
137
    daysmode => {
113
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
138
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
114
    },
139
    },
115
    lengthunit => {
140
    lengthunit => {
116
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
141
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
142
        default_value => 'days',
117
    },
143
    },
118
    maxissueqty => {
144
    maxissueqty => {
119
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
145
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
146
        default_value => "",
120
    },
147
    },
121
    maxonsiteissueqty => {
148
    maxonsiteissueqty => {
122
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
149
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
150
        default_value => "",
123
    },
151
    },
124
    maxsuspensiondays => {
152
    maxsuspensiondays => {
125
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
153
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
154
        default_value => "",
126
    },
155
    },
127
    no_auto_renewal_after => {
156
    no_auto_renewal_after => {
128
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
157
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
158
        default_value => "",
129
    },
159
    },
130
    no_auto_renewal_after_hard_limit => {
160
    no_auto_renewal_after_hard_limit => {
131
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
161
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
162
        default_value => "",
132
    },
163
    },
133
    norenewalbefore => {
164
    norenewalbefore => {
134
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
165
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
166
        default_value => "",
135
    },
167
    },
136
    onshelfholds => {
168
    onshelfholds => {
137
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
169
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
170
        default_value => 0,
138
    },
171
    },
139
    opacitemholds => {
172
    opacitemholds => {
140
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
173
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
174
        default_value => "N",
141
    },
175
    },
142
    overduefinescap => {
176
    overduefinescap => {
143
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
177
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
178
        default_value => "",
144
    },
179
    },
145
    renewalperiod => {
180
    renewalperiod => {
146
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
181
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
182
        default_value => 0,
147
    },
183
    },
148
    renewalsallowed => {
184
    renewalsallowed => {
149
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
185
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
186
        default_value => 0,
150
    },
187
    },
151
    rentaldiscount => {
188
    rentaldiscount => {
152
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
189
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
190
        default_value => 0,
153
    },
191
    },
154
    reservesallowed => {
192
    reservesallowed => {
155
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
193
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
194
        default_value => 0,
156
    },
195
    },
157
    suspension_chargeperiod => {
196
    suspension_chargeperiod => {
158
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
197
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
Lines 246-251 sub get_effective_rules { Link Here
246
285
247
=head3 set_rule
286
=head3 set_rule
248
287
288
=over 4
289
Koha::CirculationRules->set_rule( {
290
    branchcode => $branchcode,
291
    [ categorycode => $categorycode, ]
292
    [ itemtype => $itemtype, ]
293
    rule_name => $rule_name,
294
    rule_value => $rule_value,
295
    [ allow_null_out_of_scope => 1, ]
296
} )
297
=back
298
299
Sets a single circulation rule.
300
301
It is an error to specify a C<categorycode> or C<itemtype> unless the given C<rule_name> can be set
302
at that scope; i.e., you cannot set a C<patron_maxissueqty> for an itemtype.
303
304
If C<allow_null_out_of_scope> is passed, and any excess C<categorycode> or C<itemtype> is set to C<undef>,
305
they are ignored.
306
249
=cut
307
=cut
250
308
251
sub set_rule {
309
sub set_rule {
Lines 269-275 sub set_rule { Link Here
269
                unless exists $params->{$scope_level};
327
                unless exists $params->{$scope_level};
270
        } else {
328
        } else {
271
            croak "set_rule cannot set '$params->{rule_name}' for a '$scope_level'!"
329
            croak "set_rule cannot set '$params->{rule_name}' for a '$scope_level'!"
272
                if exists $params->{$scope_level};
330
                unless !exists $params->{$scope_level} || (
331
                    $params->{allow_null_out_of_scope} &&
332
                    !defined $params->{$scope_level}
333
                );
273
        }
334
        }
274
    }
335
    }
275
336
(-)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 18-23 Link Here
18
    "maxLength": 10,
18
    "maxLength": 10,
19
    "minLength": 1
19
    "minLength": 1
20
  },
20
  },
21
  "branchcode": {
22
    "type": "string",
23
    "description": "library assigned branch identifier"
24
  },
21
  "cardnumber": {
25
  "cardnumber": {
22
    "type": ["string", "null"],
26
    "type": ["string", "null"],
23
    "description": "library assigned user identifier"
27
    "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