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

(-)a/Koha/REST/V1/Reserve.pm (-61 / +107 lines)
Lines 44-113 sub add { Link Here
44
44
45
    my $body = $c->req->json;
45
    my $body = $c->req->json;
46
46
47
    my $borrowernumber = $body->{borrowernumber};
47
    my $placed;
48
    my $biblionumber = $body->{biblionumber};
48
    my $sorted_requests;
49
    my $itemnumber = $body->{itemnumber};
49
    foreach my $requested (@$body) {
50
    my $branchcode = $body->{branchcode};
50
        my $key = $requested->{borrowernumber} . '_' . $requested->{biblionumber};
51
    my $expirationdate = $body->{expirationdate};
51
        push @{ $sorted_requests->{$key} }, $requested;
52
53
    my $borrower = Koha::Borrowers->find($borrowernumber);
54
    unless ($borrower) {
55
        return $c->$cb({error => "Borrower not found"}, 404);
56
    }
52
    }
57
53
58
    unless ($biblionumber or $itemnumber) {
54
    foreach my $key (keys %$sorted_requests) {
59
        return $c->$cb({
55
        my $requests = $sorted_requests->{$key};
60
            error => "At least one of biblionumber, itemnumber should be given"
56
        my ($borrowernumber, $biblionumber) = split('_', $key);
61
        }, 400);
57
62
    }
58
        my $holds = Koha::Holds->search({
63
    unless ($branchcode) {
59
            borrowernumber => $borrowernumber,
64
        return $c->$cb({
60
            biblionumber   => $biblionumber,
65
            error => "Branchcode is required"
61
            found          => undef
66
        }, 400);
62
        });
63
64
        my $max_for_record = GetMaxPatronHoldsForRecord($borrowernumber, $biblionumber);
65
        if ($holds->count() + scalar(@$requests) > $max_for_record) {
66
            return $c->$cb({
67
                error => "Too many holds for this record"
68
            }, 400);
69
        }
70
71
        my @toreserve;
72
        my $hold_level = $holds->forced_hold_level();
73
        foreach my $request (@$requests) {
74
            my $itemnumber = $request->{itemnumber};
75
            my $branchcode = $request->{branchcode};
76
            my $expirationdate = $request->{expirationdate};
77
78
            my $level = $itemnumber ? 'item' : 'record';
79
            if ($hold_level && $hold_level ne $level) {
80
                return $c->$cb({
81
                    error => "Differents holds level for the same record."
82
                }, 400);
83
            }
84
            $hold_level = $level;
85
86
            my $borrower = Koha::Borrowers->find($borrowernumber);
87
            unless ($borrower) {
88
                return $c->$cb({error => "Borrower not found"}, 404);
89
            }
90
91
            unless ($biblionumber or $itemnumber) {
92
                return $c->$cb({
93
                    error => "At least one of biblionumber, itemnumber should be given"
94
                }, 400);
95
            }
96
            unless ($branchcode) {
97
                return $c->$cb({
98
                    error => "Branchcode is required"
99
                }, 400);
100
            }
101
102
            my $item_biblionumber = C4::Biblio::GetBiblionumberFromItemnumber($itemnumber);
103
            if ($itemnumber and $biblionumber and $biblionumber != $item_biblionumber) {
104
                return $c->$cb({
105
                    error => "Item $itemnumber doesn't belong to biblio $biblionumber"
106
                }, 400);
107
            }
108
109
            $biblionumber ||= $item_biblionumber;
110
            my $biblio = C4::Biblio::GetBiblio($biblionumber);
111
112
            my $can_reserve =
113
              $itemnumber
114
              ? CanItemBeReserved( $borrowernumber, $itemnumber )
115
              : CanBookBeReserved( $borrowernumber, $biblionumber );
116
117
            unless ($can_reserve eq 'OK') {
118
                return $c->$cb({
119
                    error => "Reserve cannot be placed. Reason: $can_reserve"
120
                }, 403);
121
            }
122
123
            my $priority = C4::Reserves::CalculatePriority($biblionumber);
124
            $itemnumber ||= undef;
125
126
            # AddReserve expects date to be in syspref format
127
            if ($expirationdate) {
128
                $expirationdate = output_pref(dt_from_string($expirationdate, 'iso'));
129
            }
130
131
            push @toreserve, {
132
                branchcode => $branchcode,
133
                borrowernumber => $borrowernumber,
134
                biblionumber => $biblionumber,
135
                priority => $priority,
136
                expirationdate => $expirationdate,
137
                title => $biblio->{title},
138
                itemnumber => $itemnumber,
139
            };
140
        }
141
142
        foreach my $r (@toreserve) {
143
            my $reserve_id = C4::Reserves::AddReserve($r->{branchcode},
144
                $r->{borrowernumber}, $r->{biblionumber}, undef, $r->{priority}, undef,
145
                $r->{expirationdate}, undef, $r->{title}, $r->{itemnumber});
146
147
            unless ($reserve_id) {
148
                return $c->$cb({
149
                    error => "Error while placing reserve. See Koha logs for details."
150
                }, 500);
151
            }
152
153
            push @{ $placed }, C4::Reserves::GetReserve($reserve_id);
154
        }
67
    }
155
    }
68
156
    return $c->$cb($placed, 201);
69
    my $item_biblionumber = C4::Biblio::GetBiblionumberFromItemnumber($itemnumber);
70
    if ($biblionumber and $biblionumber != $item_biblionumber) {
71
        return $c->$cb({
72
            error => "Item $itemnumber doesn't belong to biblio $biblionumber"
73
        }, 400);
74
    }
75
76
    $biblionumber ||= $item_biblionumber;
77
    my $biblio = C4::Biblio::GetBiblio($biblionumber);
78
79
    my $can_reserve =
80
      $itemnumber
81
      ? CanItemBeReserved( $borrowernumber, $itemnumber )
82
      : CanBookBeReserved( $borrowernumber, $biblionumber );
83
84
    unless ($can_reserve eq 'OK') {
85
        return $c->$cb({
86
            error => "Reserve cannot be placed. Reason: $can_reserve"
87
        }, 403);
88
    }
89
90
    my $priority = C4::Reserves::CalculatePriority($biblionumber);
91
    $itemnumber ||= undef;
92
93
    # AddReserve expects date to be in syspref format
94
    if ($expirationdate) {
95
        $expirationdate = output_pref(dt_from_string($expirationdate, 'iso'));
96
    }
97
98
    my $reserve_id = C4::Reserves::AddReserve($branchcode, $borrowernumber,
99
        $biblionumber, undef, $priority, undef, $expirationdate, undef,
100
        $biblio->{title}, $itemnumber);
101
102
    unless ($reserve_id) {
103
        return $c->$cb({
104
            error => "Error while placing reserve. See Koha logs for details."
105
        }, 500);
106
    }
107
108
    my $reserve = C4::Reserves::GetReserve($reserve_id);
109
110
    return $c->$cb($reserve, 201);
111
}
157
}
112
158
113
sub edit {
159
sub edit {
(-)a/api/v1/swagger.json (-24 / +30 lines)
Lines 109-136 Link Here
109
            "description": "A JSON object containing informations about the new reserve",
109
            "description": "A JSON object containing informations about the new reserve",
110
            "required": true,
110
            "required": true,
111
            "schema": {
111
            "schema": {
112
              "type": "object",
112
              "type": "array",
113
              "properties": {
113
              "items": {
114
                "borrowernumber": {
114
                "type": "object",
115
                  "description": "Borrower internal identifier",
115
                "properties": {
116
                  "type": "integer"
116
                  "borrowernumber": {
117
                },
117
                    "description": "Borrower internal identifier",
118
                "biblionumber": {
118
                    "type": "integer"
119
                  "description": "Biblio internal identifier",
119
                  },
120
                  "type": "integer"
120
                  "biblionumber": {
121
                },
121
                    "description": "Biblio internal identifier",
122
                "itemnumber": {
122
                    "type": "integer"
123
                  "description": "Item internal identifier",
123
                  },
124
                  "type": "integer"
124
                  "itemnumber": {
125
                },
125
                    "description": "Item internal identifier",
126
                "branchcode": {
126
                    "type": "integer"
127
                  "description": "Pickup location",
127
                  },
128
                  "type": "string"
128
                  "branchcode": {
129
                },
129
                    "description": "Pickup location",
130
                "expirationdate": {
130
                    "type": "string"
131
                  "description": "Reserve end date",
131
                  },
132
                  "type": "string",
132
                  "expirationdate": {
133
                  "format": "date"
133
                    "description": "Reserve end date",
134
                    "type": "string",
135
                    "format": "date"
136
                  }
134
                }
137
                }
135
              }
138
              }
136
            }
139
            }
Lines 140-147 Link Here
140
        "produces": ["application/json"],
143
        "produces": ["application/json"],
141
        "responses": {
144
        "responses": {
142
          "201": {
145
          "201": {
143
            "description": "Created reserve",
146
            "description": "Created reserves",
144
            "schema": { "$ref": "#/definitions/reserve" }
147
            "schema": {
148
                "type": "array",
149
                "items": { "$ref": "#/definitions/reserve" }
150
            }
145
          },
151
          },
146
          "400": {
152
          "400": {
147
            "description": "Missing or wrong parameters",
153
            "description": "Missing or wrong parameters",
(-)a/t/db_dependent/api/v1/reserves.t (-10 / +59 lines)
Lines 17-24 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 30;
20
use Test::More tests => 43;
21
use Test::Mojo;
21
use Test::Mojo;
22
use t::lib::TestBuilder;
22
23
23
use DateTime;
24
use DateTime;
24
25
Lines 34-43 my $dbh = C4::Context->dbh; Link Here
34
$dbh->{AutoCommit} = 0;
35
$dbh->{AutoCommit} = 0;
35
$dbh->{RaiseError} = 1;
36
$dbh->{RaiseError} = 1;
36
37
38
my $builder = t::lib::TestBuilder->new();
37
my $t = Test::Mojo->new('Koha::REST::V1');
39
my $t = Test::Mojo->new('Koha::REST::V1');
38
40
39
my $categorycode = Koha::Database->new()->schema()->resultset('Category')->first()->categorycode();
41
my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
40
my $branchcode = Koha::Database->new()->schema()->resultset('Branch')->first()->branchcode();
42
my $categorycode2 = $builder->build({ source => 'Category' })->{ categorycode };
43
my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode };
41
44
42
my $borrower = Koha::Borrower->new;
45
my $borrower = Koha::Borrower->new;
43
$borrower->categorycode( $categorycode );
46
$borrower->categorycode( $categorycode );
Lines 47-53 $borrower->store; Link Here
47
my $borrowernumber = $borrower->borrowernumber;
50
my $borrowernumber = $borrower->borrowernumber;
48
51
49
my $borrower2 = Koha::Borrower->new;
52
my $borrower2 = Koha::Borrower->new;
50
$borrower2->categorycode( $categorycode );
53
$borrower2->categorycode( $categorycode2 );
51
$borrower2->branchcode( $branchcode );
54
$borrower2->branchcode( $branchcode );
52
$borrower2->surname("Test Surname 2");
55
$borrower2->surname("Test Surname 2");
53
$borrower2->store;
56
$borrower2->store;
Lines 55-60 my $borrowernumber2 = $borrower2->borrowernumber; Link Here
55
58
56
my $biblionumber = create_biblio('RESTful Web APIs');
59
my $biblionumber = create_biblio('RESTful Web APIs');
57
my $itemnumber = create_item($biblionumber, 'TEST000001');
60
my $itemnumber = create_item($biblionumber, 'TEST000001');
61
my $itemnumber2 = create_item($biblionumber, 'TEST000002');
62
63
my $biblionumber2 = create_biblio('Calamity');
64
my $itemnumber3 = create_item($biblionumber2, 'TEST000003');
65
my $itemnumber4 = create_item($biblionumber2, 'TEST000004');
66
my $itemnumber5 = create_item($biblionumber2, 'TEST000005');
58
67
59
my $reserve_id = C4::Reserves::AddReserve($branchcode, $borrowernumber,
68
my $reserve_id = C4::Reserves::AddReserve($branchcode, $borrowernumber,
60
    $biblionumber, undef, 1, undef, undef, undef, '', $itemnumber);
69
    $biblionumber, undef, 1, undef, undef, undef, '', $itemnumber);
Lines 99-119 $dbh->do('DELETE FROM issuingrules'); Link Here
99
$dbh->do(q{
108
$dbh->do(q{
100
    INSERT INTO issuingrules (categorycode, branchcode, itemtype, reservesallowed)
109
    INSERT INTO issuingrules (categorycode, branchcode, itemtype, reservesallowed)
101
    VALUES (?, ?, ?, ?)
110
    VALUES (?, ?, ?, ?)
102
}, {}, '*', '*', '*', 1);
111
}, {}, $categorycode, '*', '*', 1);
112
113
$dbh->do(q{
114
    INSERT INTO issuingrules (categorycode, branchcode, itemtype, holds_per_record, reservesallowed)
115
    VALUES (?, ?, ?, ?, ?)
116
}, {}, $categorycode2, '*', '*', '3', 5);
103
117
104
my $expirationdate = DateTime->now->add(days => 10)->ymd;
118
my $expirationdate = DateTime->now->add(days => 10)->ymd;
105
my $post_data = {
119
my $post_data = [{
106
    borrowernumber => int($borrowernumber),
120
    borrowernumber => int($borrowernumber),
107
    biblionumber => int($biblionumber),
121
    biblionumber => int($biblionumber),
108
    itemnumber => int($itemnumber),
122
    itemnumber => int($itemnumber),
109
    branchcode => $branchcode,
123
    branchcode => $branchcode,
110
    expirationdate => $expirationdate,
124
    expirationdate => $expirationdate,
111
};
125
}];
112
$t->post_ok("/api/v1/reserves" => json => $post_data)
126
$t->post_ok("/api/v1/reserves" => json => $post_data)
113
  ->status_is(201)
127
  ->status_is(201)
114
  ->json_has('/reserve_id');
128
  ->json_has('/0/reserve_id');
115
129
116
$reserve_id = $t->tx->res->json->{reserve_id};
130
$reserve_id = $t->tx->res->json->[0]->{reserve_id};
117
131
118
$t->get_ok("/api/v1/reserves?borrowernumber=$borrowernumber")
132
$t->get_ok("/api/v1/reserves?borrowernumber=$borrowernumber")
119
  ->status_is(200)
133
  ->status_is(200)
Lines 121-130 $t->get_ok("/api/v1/reserves?borrowernumber=$borrowernumber") Link Here
121
  ->json_is('/0/expirationdate', $expirationdate)
135
  ->json_is('/0/expirationdate', $expirationdate)
122
  ->json_is('/0/branchcode', $branchcode);
136
  ->json_is('/0/branchcode', $branchcode);
123
137
138
$post_data->[0]->{biblionumber} = int($biblionumber2);
139
$post_data->[0]->{itemnumber} = int($itemnumber3);
124
$t->post_ok("/api/v1/reserves" => json => $post_data)
140
$t->post_ok("/api/v1/reserves" => json => $post_data)
125
  ->status_is(403)
141
  ->status_is(403)
126
  ->json_like('/error', qr/tooManyReserves/);
142
  ->json_like('/error', qr/tooManyReserves/);
127
143
144
$post_data = [
145
    {
146
        borrowernumber => int($borrowernumber2),
147
        biblionumber => int($biblionumber2),
148
        itemnumber => int($itemnumber3),
149
        branchcode => $branchcode,
150
    },
151
    {
152
        borrowernumber => int($borrowernumber2),
153
        biblionumber => int($biblionumber2),
154
        branchcode => $branchcode,
155
    },
156
];
157
158
$t->post_ok("/api/v1/reserves" => json => $post_data)
159
  ->status_is(400)
160
  ->json_like('/error', qr/Differents holds level/);
161
162
delete $post_data->[0]->{itemnumber};
163
$t->post_ok("/api/v1/reserves" => json => $post_data)
164
  ->status_is(201)
165
  ->json_has('/0/reserve_id')
166
  ->json_has('/1/reserve_id');
167
168
$t->post_ok("/api/v1/reserves" => json => $post_data)
169
  ->status_is(400)
170
  ->json_like('/error', qr/Too many holds/);
171
172
delete $post_data->[1];
173
$post_data->[0]->{itemnumber} = int($itemnumber3);
174
$t->post_ok("/api/v1/reserves" => json => $post_data)
175
  ->status_is(400)
176
  ->json_like('/error', qr/Differents holds level/);
177
128
178
129
$dbh->rollback;
179
$dbh->rollback;
130
180
131
- 

Return to bug 15567