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

(-)a/Koha/REST/V1/Hold.pm (-88 / +331 lines)
Lines 27-166 use Koha::Patrons; Link Here
27
use Koha::Holds;
27
use Koha::Holds;
28
use Koha::DateUtils;
28
use Koha::DateUtils;
29
29
30
use Try::Tiny;
31
32
=head1 API
33
34
=head2 Class methods
35
36
=head3 list
37
38
Mehtod that handles listing Koha::Hold objects
39
40
=cut
41
30
sub list {
42
sub list {
31
    my $c = shift->openapi->valid_input or return;
43
    my $c = shift->openapi->valid_input or return;
32
44
33
    my $params = $c->req->query_params->to_hash;
45
    return try {
34
    my @valid_params = Koha::Holds->_resultset->result_source->columns;
46
        my $holds_set = Koha::Holds->new;
35
    foreach my $key (keys %$params) {
47
        my $holds     = $c->objects->search( $holds_set, \&_to_model, \&_to_api );
36
        delete $params->{$key} unless grep { $key eq $_ } @valid_params;
48
        return $c->render( status => 200, openapi => $holds );
37
    }
49
    }
38
    my $holds = Koha::Holds->search($params);
50
    catch {
39
51
        if ( blessed $_ && $_->isa('Koha::Exceptions') ) {
40
    return $c->render(status => 200, openapi => $holds);
52
            return $c->render(
53
                status  => 500,
54
                openapi => { error => "$_" }
55
            );
56
        }
57
        else {
58
            return $c->render(
59
                status  => 500,
60
                openapi => { error => "Something went wrong, check Koha logs for details." }
61
            );
62
        }
63
    };
41
}
64
}
42
65
43
sub add {
66
=head3 add
44
    my $c = shift->openapi->valid_input or return;
45
67
46
    my $body = $c->req->json;
68
Method that handles adding a new Koha::Hold object
47
69
48
    my $borrowernumber = $body->{borrowernumber};
70
=cut
49
    my $biblionumber = $body->{biblionumber};
50
    my $itemnumber = $body->{itemnumber};
51
    my $branchcode = $body->{branchcode};
52
    my $expirationdate = $body->{expirationdate};
53
    my $itemtype = $body->{itemtype};
54
71
55
    my $borrower = Koha::Patrons->find($borrowernumber);
72
sub add {
56
    unless ($borrower) {
73
    my $c = shift->openapi->valid_input or return;
57
        return $c->render( status  => 404,
58
                           openapi => {error => "Borrower not found"} );
59
    }
60
74
61
    unless ($biblionumber or $itemnumber) {
75
    return try {
62
        return $c->render( status => 400, openapi => {
76
        my $body = $c->validation->param('body');
63
            error => "At least one of biblionumber, itemnumber should be given"
77
64
        } );
78
        my $biblio;
65
    }
79
66
    unless ($branchcode) {
80
        my $biblio_id         = $body->{biblio_id};
67
        return $c->render( status  => 400,
81
        my $pickup_library_id = $body->{pickup_library_id};
68
                           openapi => { error => "Branchcode is required" } );
82
        my $item_id           = $body->{item_id};
69
    }
83
        my $patron_id         = $body->{patron_id};
84
        my $item_type         = $body->{item_type};
85
        my $expiration_date   = $body->{expiration_date};
86
        my $notes             = $body->{notes};
87
88
        if ( $item_id and $biblio_id ) {
89
90
            # check they are consistent
91
            unless ( Koha::Items->search( { itemnumber => $item_id, biblionumber => $biblio_id } )
92
                ->count > 0 )
93
            {
94
                return $c->render(
95
                    status  => 400,
96
                    openapi => { error => "Item $item_id doesn't belong to biblio $biblio_id" }
97
                );
98
            }
99
            else {
100
                $biblio = Koha::Biblios->find($biblio_id);
101
            }
102
        }
103
        elsif ($item_id) {
104
            my $item = Koha::Items->find($item_id);
105
106
            unless ($item) {
107
                return $c->render(
108
                    status  => 404,
109
                    openapi => { error => "item_id not found." }
110
                );
111
            }
112
            else {
113
                $biblio = $item->biblio;
114
            }
115
        }
116
        elsif ($biblio_id) {
117
            $biblio = Koha::Biblios->find($biblio_id);
118
        }
119
        else {
120
            return $c->render(
121
                status  => 400,
122
                openapi => { error => "At least one of biblio_id, item_id should be given" }
123
            );
124
        }
70
125
71
    my $biblio;
126
        unless ($biblio) {
72
    if ($itemnumber) {
73
        my $item = Koha::Items->find( $itemnumber );
74
        $biblio = $item->biblio;
75
        if ($biblionumber and $biblionumber != $biblio->biblionumber) {
76
            return $c->render(
127
            return $c->render(
77
                status => 400,
128
                status  => 400,
78
                openapi => {
129
                openapi => "Biblio not found."
79
                    error => "Item $itemnumber doesn't belong to biblio $biblionumber"
130
            );
80
                });
131
        }
81
        }
82
        $biblionumber ||= $biblio->biblionumber;
83
    } else {
84
        $biblio = Koha::Biblios->find( $biblionumber );
85
    }
86
132
87
    my $can_reserve =
133
        my $can_place_hold
88
      $itemnumber
134
            = $item_id
89
      ? CanItemBeReserved( $borrowernumber, $itemnumber )
135
            ? C4::Reserves::CanItemBeReserved( $patron_id, $item_id )
90
      : CanBookBeReserved( $borrowernumber, $biblionumber );
136
            : C4::Reserves::CanBookBeReserved( $patron_id, $biblio_id );
91
137
92
    unless ($can_reserve->{status} eq 'OK') {
138
        unless ( $can_place_hold->{status} eq 'OK' ) {
93
        return $c->render( status => 403, openapi => {
139
            return $c->render(
94
            error => "Reserve cannot be placed. Reason: ". $can_reserve->{status}
140
                status => 403,
95
        } );
141
                openapi =>
96
    }
142
                    { error => "Hold cannot be placed. Reason: " . $can_place_hold->{status} }
143
            );
144
        }
97
145
98
    my $priority = C4::Reserves::CalculatePriority($biblionumber);
146
        my $priority = C4::Reserves::CalculatePriority($biblio_id);
99
    $itemnumber ||= undef;
100
147
101
    # AddReserve expects date to be in syspref format
148
        # AddReserve expects date to be in syspref format
102
    if ($expirationdate) {
149
        if ($expiration_date) {
103
        $expirationdate = output_pref(dt_from_string($expirationdate, 'iso'));
150
            $expiration_date = output_pref( dt_from_string( $expiration_date, 'rfc3339' ) );
104
    }
151
        }
105
152
106
    my $reserve_id = C4::Reserves::AddReserve($branchcode, $borrowernumber,
153
        my $hold_id = C4::Reserves::AddReserve(
107
        $biblionumber, undef, $priority, undef, $expirationdate, undef,
154
            $pickup_library_id,
108
        $biblio->title, $itemnumber, undef, $itemtype);
155
            $patron_id,
156
            $biblio_id,
157
            undef,    # $bibitems param is unused
158
            $priority,
159
            undef,    # hold date, we don't allow it currently
160
            $expiration_date,
161
            $notes,
162
            $biblio->title,
163
            $item_id,
164
            undef,    # TODO: Why not?
165
            $item_type
166
        );
167
168
        unless ($hold_id) {
169
            return $c->render(
170
                status  => 500,
171
                openapi => 'Error placing the hold. See Koha logs for details.'
172
            );
173
        }
109
174
110
    unless ($reserve_id) {
175
        my $hold = Koha::Holds->find($hold_id);
111
        return $c->render( status => 500, openapi => {
176
112
            error => "Error while placing reserve. See Koha logs for details."
177
        return $c->render( status => 201, openapi => _to_api($hold->TO_JSON) );
113
        } );
114
    }
178
    }
179
    catch {
180
        if ( blessed $_ and $_->isa('Koha::Exceptions') ) {
181
            if ( $_->isa('Koha::Exceptions::Object::FKConstraint') ) {
182
                my $broken_fk = $_->broken_fk;
183
184
                if ( grep { $_ eq $broken_fk } keys %{$Koha::REST::V1::Hold::to_api_mapping} ) {
185
                    $c->render(
186
                        status  => 404,
187
                        openapi => $Koha::REST::V1::Hold::to_api_mapping->{$broken_fk} . ' not found.'
188
                    );
189
                }
190
                else {
191
                    return $c->render(
192
                        status  => 500,
193
                        openapi => { error => "Uncaught exception: $_" }
194
                    );
195
                }
196
            }
197
            else {
198
                return $c->render(
199
                    status  => 500,
200
                    openapi => { error => "$_" }
201
                );
202
            }
203
        }
204
        else {
205
            return $c->render(
206
                status  => 500,
207
                openapi => { error => "Something went wrong. check the logs." }
208
            );
209
        }
210
    };
211
}
115
212
116
    my $reserve = Koha::Holds->find($reserve_id);
213
=head3 edit
117
214
118
    return $c->render( status => 201, openapi => $reserve );
215
Method that handles modifying a Koha::Hold object
119
}
216
217
=cut
120
218
121
sub edit {
219
sub edit {
122
    my $c = shift->openapi->valid_input or return;
220
    my $c = shift->openapi->valid_input or return;
123
221
124
    my $reserve_id = $c->validation->param('reserve_id');
222
    my $hold_id = $c->validation->param('hold_id');
125
    my $hold = Koha::Holds->find( $reserve_id );
223
    my $hold = Koha::Holds->find( $hold_id );
126
224
127
    unless ($hold) {
225
    unless ($hold) {
128
        return $c->render( status  => 404,
226
        return $c->render( status  => 404,
129
                           openapi => {error => "Reserve not found"} );
227
                           openapi => {error => "Hold not found"} );
130
    }
228
    }
131
229
132
    my $body = $c->req->json;
230
    my $body = $c->req->json;
133
231
134
    my $branchcode = $body->{branchcode};
232
    my $pickup_library_id = $body->{pickup_library_id};
135
    my $priority = $body->{priority};
233
    my $priority          = $body->{priority};
136
    my $suspend_until = $body->{suspend_until};
234
    my $suspended_until   = $body->{suspended_until};
137
235
138
    if ($suspend_until) {
236
    if ($suspended_until) {
139
        $suspend_until = output_pref(dt_from_string($suspend_until, 'iso'));
237
        $suspended_until = output_pref(dt_from_string($suspended_until, 'rfc3339'));
140
    }
238
    }
141
239
142
    my $params = {
240
    my $params = {
143
        reserve_id => $reserve_id,
241
        reserve_id    => $hold_id,
144
        branchcode => $branchcode,
242
        branchcode    => $pickup_library_id,
145
        rank => $priority,
243
        rank          => $priority,
146
        suspend_until => $suspend_until,
244
        suspend_until => $suspended_until,
147
        itemnumber => $hold->itemnumber
245
        itemnumber    => $hold->itemnumber
148
    };
246
    };
149
247
150
    C4::Reserves::ModReserve($params);
248
    C4::Reserves::ModReserve($params);
151
    $hold = Koha::Holds->find($reserve_id);
249
    $hold->discard_changes; # refresh
152
250
153
    return $c->render( status => 200, openapi => $hold );
251
    return $c->render( status => 200, openapi => _to_api( $hold->TO_JSON ) );
154
}
252
}
155
253
254
=head3 delete
255
256
Method that handles deleting a Koha::Hold object
257
258
=cut
259
156
sub delete {
260
sub delete {
157
    my $c = shift->openapi->valid_input or return;
261
    my $c = shift->openapi->valid_input or return;
158
262
159
    my $reserve_id = $c->validation->param('reserve_id');
263
    my $hold_id = $c->validation->param('hold_id');
160
    my $hold = Koha::Holds->find( $reserve_id );
264
    my $hold    = Koha::Holds->find($hold_id);
161
265
162
    unless ($hold) {
266
    unless ($hold) {
163
        return $c->render( status => 404, openapi => {error => "Reserve not found"} );
267
        return $c->render( status => 404, openapi => { error => "Hold not found." } );
164
    }
268
    }
165
269
166
    $hold->cancel;
270
    $hold->cancel;
Lines 168-171 sub delete { Link Here
168
    return $c->render( status => 200, openapi => {} );
272
    return $c->render( status => 200, openapi => {} );
169
}
273
}
170
274
275
276
=head3 _to_api
277
278
Helper function that maps unblessed Koha::Hold objects into REST api
279
attribute names.
280
281
=cut
282
283
sub _to_api {
284
    my $hold = shift;
285
286
    # Rename attributes
287
    foreach my $column ( keys %{ $Koha::REST::V1::Hold::to_api_mapping } ) {
288
        my $mapped_column = $Koha::REST::V1::Hold::to_api_mapping->{$column};
289
        if (    exists $hold->{ $column }
290
             && defined $mapped_column )
291
        {
292
            # key != undef
293
            $hold->{ $mapped_column } = delete $hold->{ $column };
294
        }
295
        elsif (    exists $hold->{ $column }
296
                && !defined $mapped_column )
297
        {
298
            # key == undef
299
            delete $hold->{ $column };
300
        }
301
    }
302
303
    return $hold;
304
}
305
306
=head3 _to_model
307
308
Helper function that maps REST api objects into Koha::Hold
309
attribute names.
310
311
=cut
312
313
sub _to_model {
314
    my $hold = shift;
315
316
    foreach my $attribute ( keys %{ $Koha::REST::V1::Hold::to_model_mapping } ) {
317
        my $mapped_attribute = $Koha::REST::V1::Hold::to_model_mapping->{$attribute};
318
        if (    exists $hold->{ $attribute }
319
             && defined $mapped_attribute )
320
        {
321
            # key => !undef
322
            $hold->{ $mapped_attribute } = delete $hold->{ $attribute };
323
        }
324
        elsif (    exists $hold->{ $attribute }
325
                && !defined $mapped_attribute )
326
        {
327
            # key => undef / to be deleted
328
            delete $hold->{ $attribute };
329
        }
330
    }
331
332
    if ( exists $hold->{lowestPriority} ) {
333
        $hold->{lowestPriority} = ($hold->{lowestPriority}) ? 1 : 0;
334
    }
335
336
    if ( exists $hold->{suspend} ) {
337
        $hold->{suspend} = ($hold->{suspend}) ? 1 : 0;
338
    }
339
340
    if ( exists $hold->{reservedate} ) {
341
        $hold->{reservedate} = output_pref({ str => $hold->{reservedate}, dateformat => 'sql' });
342
    }
343
344
    if ( exists $hold->{cancellationdate} ) {
345
        $hold->{cancellationdate} = output_pref({ str => $hold->{cancellationdate}, dateformat => 'sql' });
346
    }
347
348
    if ( exists $hold->{timestamp} ) {
349
        $hold->{timestamp} = output_pref({ str => $hold->{timestamp}, dateformat => 'sql' });
350
    }
351
352
    if ( exists $hold->{waitingdate} ) {
353
        $hold->{waitingdate} = output_pref({ str => $hold->{waitingdate}, dateformat => 'sql' });
354
    }
355
356
    if ( exists $hold->{expirationdate} ) {
357
        $hold->{expirationdate} = output_pref({ str => $hold->{expirationdate}, dateformat => 'sql' });
358
    }
359
360
    if ( exists $hold->{suspend_until} ) {
361
        $hold->{suspend_until} = output_pref({ str => $hold->{suspend_until}, dateformat => 'sql' });
362
    }
363
364
    return $hold;
365
}
366
367
=head2 Global variables
368
369
=head3 $to_api_mapping
370
371
=cut
372
373
our $to_api_mapping = {
374
    reserve_id       => 'hold_id',
375
    borrowernumber   => 'patron_id',
376
    reservedate      => 'hold_date',
377
    biblionumber     => 'biblio_id',
378
    branchcode       => 'pickup_library_id',
379
    notificationdate => undef,
380
    reminderdate     => undef,
381
    cancellationdate => 'cancelation_date',
382
    reservenotes     => 'notes',
383
    found            => 'status',
384
    itemnumber       => 'item_id',
385
    waitingdate      => 'waiting_date',
386
    expirationdate   => 'expiration_date',
387
    lowestPriority   => 'lowest_priority',
388
    suspend          => 'suspended',
389
    suspend_until    => 'suspended_until',
390
    itemtype         => 'item_type',
391
};
392
393
=head3 $to_model_mapping
394
395
=cut
396
397
our $to_model_mapping = {
398
    hold_id           => 'reserve_id',
399
    patron_id         => 'borrowernumber',
400
    hold_date         => 'reservedate',
401
    biblio_id         => 'biblionumber',
402
    pickup_library_id => 'branchcode',
403
    cancelation_date  => 'cancellationdate',
404
    notes             => 'reservenotes',
405
    status            => 'found',
406
    item_id           => 'itemnumber',
407
    waiting_date      => 'waitingdate',
408
    expiration_date   => 'expirationdate',
409
    lowest_priority   => 'lowestPriority',
410
    suspended         => 'suspend',
411
    suspended_until   => 'suspend_until',
412
    item_type         => 'itemtype',
413
};
414
171
1;
415
1;
172
- 

Return to bug 20006