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

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

Return to bug 20006