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

(-)a/Koha/Booking.pm (-118 / +45 lines)
Lines 143-149 sub store { Link Here
143
                    booking_id => $self->in_storage ? $self->booking_id : undef
143
                    booking_id => $self->in_storage ? $self->booking_id : undef
144
                }
144
                }
145
                );
145
                );
146
147
            # FIXME: We should be able to combine the above two functions into one
146
            # FIXME: We should be able to combine the above two functions into one
148
147
149
            # Assign item at booking time
148
            # Assign item at booking time
Lines 151-207 sub store { Link Here
151
                $self->_assign_item_for_booking;
150
                $self->_assign_item_for_booking;
152
            }
151
            }
153
152
154
            my $is_modification = $self->in_storage;
153
            if ( !$self->in_storage ) {
155
            my $old_booking     = $self->get_from_storage;
154
                $self->SUPER::store;
156
155
                $self->discard_changes;
157
            if ( $self = $self->SUPER::store ) {
156
                $self->_send_notice( { notice => 'BOOKING_CONFIRMATION' } );
158
                my $patron         = $self->patron;
157
            } else {
159
                my $pickup_library = $self->pickup_library;
158
                my %updated_columns = $self->_result->get_dirty_columns;
160
                my $branch         = C4::Context->userenv->{'branch'};
159
                return $self->SUPER::store unless %updated_columns;
161
160
162
                if ( $is_modification
161
                my $old_booking = $self->get_from_storage;
163
                    and any { $old_booking->$_ ne $self->$_ } qw(pickup_library_id start_date end_date) )
162
                $self->SUPER::store;
163
164
                if ( exists( $updated_columns{status} ) && $updated_columns{status} eq 'cancelled' ) {
165
                    $self->_send_notice(
166
                        { notice => 'BOOKING_CANCELLATION', objects => { old_booking => $old_booking } } );
167
                } elsif ( exists( $updated_columns{pickup_library_id} )
168
                    or exists( $updated_columns{start_date} )
169
                    or exists( $updated_columns{end_date} ) )
164
                {
170
                {
165
                    my $letter = C4::Letters::GetPreparedLetter(
171
                    $self->_send_notice(
166
                        module                 => 'bookings',
172
                        { notice => 'BOOKING_MODIFICATION', objects => { old_booking => $old_booking } } );
167
                        letter_code            => 'BOOKING_MODIFICATION',
168
                        message_transport_type => 'email',
169
                        branchcode             => $branch,
170
                        lang                   => $patron->lang,
171
                        objects                => {
172
                            old_booking => $old_booking,
173
                            booking     => $self
174
                        },
175
                    );
176
177
                    if ($letter) {
178
                        C4::Letters::EnqueueLetter(
179
                            {
180
                                letter                 => $letter,
181
                                borrowernumber         => $patron->borrowernumber,
182
                                message_transport_type => 'email',
183
                            }
184
                        );
185
                    }
186
                } elsif ( !$is_modification ) {
187
                    my $letter = C4::Letters::GetPreparedLetter(
188
                        module                 => 'bookings',
189
                        letter_code            => 'BOOKING_CONFIRMATION',
190
                        message_transport_type => 'email',
191
                        branchcode             => $branch,
192
                        lang                   => $patron->lang,
193
                        objects                => { booking => $self },
194
                    );
195
196
                    if ($letter) {
197
                        C4::Letters::EnqueueLetter(
198
                            {
199
                                letter                 => $letter,
200
                                borrowernumber         => $patron->borrowernumber,
201
                                message_transport_type => 'email',
202
                            }
203
                        );
204
                    }
205
                }
173
                }
206
            }
174
            }
207
        }
175
        }
Lines 298-382 sub to_api_mapping { Link Here
298
    return {};
266
    return {};
299
}
267
}
300
268
301
=head3 edit
269
=head2 Internal methods
302
303
This method allows patching a booking
304
305
=cut
306
307
sub edit {
308
    my ( $self, $params ) = @_;
309
310
    my $new_status = $params->{'status'};
311
    unless ($new_status) {
312
        return $self->store;
313
    }
314
315
    $self->_set_status($new_status);
316
317
    my $status = $self->status;
318
    if ( $status eq 'cancelled' ) {
319
        $self->cancel( { send_letter => 1 } );
320
    }
321
270
322
    return $self->store;
271
=head3 _send_notice
323
}
324
272
325
=head3 cancel
273
    $self->_send_notice();
326
274
327
This method adds possibility of cancelling a booking (kept in table but flagged with 'cancelled' status)
275
Sends appropriate notice to patron.
328
Also adds param to send a letter to the borrower affected by the cancellation
329
276
330
=cut
277
=cut
331
278
332
sub cancel {
279
sub _send_notice {
333
    my ( $self, $params ) = @_;
280
    my ( $self, $params ) = @_;
334
281
335
    my $branch         = C4::Context->userenv->{'branch'};
282
    my $notice  = $params->{notice};
336
    my $patron         = $self->patron;
283
    my $objects = $params->{objects} // {};
337
    my $pickup_library = $self->pickup_library;
284
    $objects->{booking} = $self;
338
339
    if ( $params->{'send_letter'} ) {
340
        my $letter = C4::Letters::GetPreparedLetter(
341
            module                 => 'bookings',
342
            letter_code            => 'BOOKING_CANCELLATION',
343
            message_transport_type => 'email',
344
            branchcode             => $branch,
345
            lang                   => $patron->lang,
346
            objects                => { booking => $self }
347
        );
348
285
349
        if ($letter) {
286
    my $branch = C4::Context->userenv->{'branch'};
350
            C4::Letters::EnqueueLetter(
287
    my $patron = $self->patron;
351
                {
352
                    letter                 => $letter,
353
                    borrowernumber         => $patron->borrowernumber,
354
                    message_transport_type => 'email',
355
                }
356
            );
357
        }
358
    }
359
}
360
361
362
=head2 Internal methods
363
364
=head3 _set_status
365
288
366
This method changes the status of a booking
289
    my $letter = C4::Letters::GetPreparedLetter(
367
290
        module                 => 'bookings',
368
=cut
291
        letter_code            => $notice,
369
292
        message_transport_type => 'email',
370
sub _set_status {
293
        branchcode             => $branch,
371
    my ( $self, $new_status ) = @_;
294
        lang                   => $patron->lang,
295
        objects                => $objects
296
    );
372
297
373
    my @valid_statuses = qw(new completed cancelled);
298
    if ($letter) {
374
    my $is_valid       = any { $new_status eq $_ } @valid_statuses;
299
        C4::Letters::EnqueueLetter(
375
    unless ($is_valid) {
300
            {
376
        die "Invalid status: $new_status";
301
                letter                 => $letter,
302
                borrowernumber         => $patron->borrowernumber,
303
                message_transport_type => 'email',
304
            }
305
        );
377
    }
306
    }
378
379
    $self->status($new_status);
380
}
307
}
381
308
382
=head3 _type
309
=head3 _type
(-)a/Koha/Object.pm (+10 lines)
Lines 202-207 sub store { Link Here
202
                    property => $property =~ /(\w+\.\w+)$/ ? $1 : $property, # results in table.column without quotes or backtics
202
                    property => $property =~ /(\w+\.\w+)$/ ? $1 : $property, # results in table.column without quotes or backtics
203
                );
203
                );
204
            }
204
            }
205
            elsif ( $_->{msg} =~ /Data truncated for column \W?(?<property>\w+)/ ) {    # The optional \W in the regex might be a quote or backtick
206
                my $property = $+{property};
207
                my $type     = $columns_info->{$property}->{data_type};
208
                Koha::Exceptions::Object::BadValue->throw(
209
                    type     => 'enum',
210
                    property => $property =~ /(\w+\.\w+)$/
211
                    ? $1
212
                    : $property,    # results in table.column without quotes or backtics
213
                ) if $type eq 'enum';
214
            }
205
        }
215
        }
206
        # Catch-all for foreign key breakages. It will help find other use cases
216
        # Catch-all for foreign key breakages. It will help find other use cases
207
        $_->rethrow();
217
        $_->rethrow();
(-)a/Koha/REST/V1/Bookings.pm (-24 lines)
Lines 148-175 sub delete { Link Here
148
    };
148
    };
149
}
149
}
150
150
151
=head3 edit
152
153
Controller function that handles editing an existing booking
154
155
=cut
156
157
sub edit {
158
    my $c = shift->openapi->valid_input or return;
159
160
    my $booking = $c->objects->find_rs( Koha::Bookings->new, $c->param('booking_id') );
161
    return $c->render_resource_not_found("Booking")
162
        unless $booking;
163
164
    return try {
165
        $booking->edit( $c->req->json );
166
        return $c->render(
167
            status  => 200,
168
            openapi => $c->objects->to_api($booking),
169
        );
170
    } catch {
171
        $c->unhandled_exception($_);
172
    };
173
}
174
175
1;
151
1;
(-)a/api/v1/swagger/definitions/booking_patch.yaml (+45 lines)
Line 0 Link Here
1
---
2
additionalProperties: false
3
properties:
4
  biblio_id:
5
    description: Internal identifier for the parent bibliographic record
6
    type: integer
7
  booking_id:
8
    description: Internal booking identifier
9
    readOnly: true
10
    type: integer
11
  creation_date:
12
    description: Creation date and time of this booking
13
    readOnly: true
14
    format: date-time
15
    type: string
16
  end_date:
17
    description: Start date and time of this booking
18
    format: date-time
19
    type: string
20
  item_id:
21
    description: Internal item identifier
22
    type:
23
      - integer
24
      - 'null'
25
  modification_date:
26
    description: Modification date and time of this booking
27
    readOnly: true
28
    format: date-time
29
    type: string
30
  patron_id:
31
    description: Internal patron identifier
32
    type: integer
33
  pickup_library_id:
34
    description: Internal pickup_library identifier
35
    type: string
36
  start_date:
37
    description: Start date and time of this booking
38
    format: date-time
39
    type: string
40
  status:
41
    description: Status of the booking
42
    type:
43
      - string
44
      - "null"
45
type: object
(-)a/api/v1/swagger/paths/bookings.yaml (-14 / +11 lines)
Lines 208-224 Link Here
208
    operationId: updateBooking
208
    operationId: updateBooking
209
    parameters:
209
    parameters:
210
      - $ref: "../swagger.yaml#/parameters/booking_id_pp"
210
      - $ref: "../swagger.yaml#/parameters/booking_id_pp"
211
      - description: A booking object
211
      - description: A complete booking object to replace the current one
212
        in: body
212
        in: body
213
        name: body
213
        name: body
214
        required: true
214
        required: true
215
        schema:
215
        schema:
216
          $ref: ../swagger.yaml#/definitions/booking
216
          $ref: ../swagger.yaml#/definitions/booking
217
    consumes:
218
      - application/json
217
    produces:
219
    produces:
218
      - application/json
220
      - application/json
219
    responses:
221
    responses:
220
      200:
222
      200:
221
        description: A booking
223
        description: Updated booking
222
        schema:
224
        schema:
223
          $ref: ../swagger.yaml#/definitions/booking
225
          $ref: ../swagger.yaml#/definitions/booking
224
      400:
226
      400:
Lines 253-276 Link Here
253
        circulate: manage_bookings
255
        circulate: manage_bookings
254
    x-mojo-to: Bookings#update
256
    x-mojo-to: Bookings#update
255
  patch:
257
  patch:
256
    x-mojo-to: Bookings#edit
257
    operationId: editBooking
258
    operationId: editBooking
258
    tags:
259
      - bookings
260
    summary: Edit booking
261
    parameters:
259
    parameters:
262
      - $ref: "../swagger.yaml#/parameters/booking_id_pp"
260
      - $ref: "../swagger.yaml#/parameters/booking_id_pp"
263
      - name: body
261
      - description: A partial booking object containing fields to modify
264
        in: body
262
        in: body
265
        description: A JSON object containing fields to modify
263
        name: body
266
        required: true
264
        required: true
267
        schema:
265
        schema:
268
          type: object
266
          $ref: ../swagger.yaml#/definitions/booking_patch
269
          properties:
270
            status:
271
              description: Set booking status
272
              type: string
273
          additionalProperties: false
274
    consumes:
267
    consumes:
275
      - application/json
268
      - application/json
276
    produces:
269
    produces:
Lines 304-309 Link Here
304
        description: Under maintenance
297
        description: Under maintenance
305
        schema:
298
        schema:
306
          $ref: ../swagger.yaml#/definitions/error
299
          $ref: ../swagger.yaml#/definitions/error
300
    summary: Update booking
301
    tags:
302
      - bookings
307
    x-koha-authorization:
303
    x-koha-authorization:
308
      permissions:
304
      permissions:
309
        circulate: manage_bookings
305
        circulate: manage_bookings
306
    x-mojo-to: Bookings#update
(-)a/api/v1/swagger/swagger.yaml (+2 lines)
Lines 20-25 definitions: Link Here
20
    $ref: ./definitions/basket.yaml
20
    $ref: ./definitions/basket.yaml
21
  booking:
21
  booking:
22
    $ref: ./definitions/booking.yaml
22
    $ref: ./definitions/booking.yaml
23
  booking_patch:
24
    $ref: ./definitions/booking_patch.yaml
23
  bundle_link:
25
  bundle_link:
24
    $ref: ./definitions/bundle_link.yaml
26
    $ref: ./definitions/bundle_link.yaml
25
  cash_register:
27
  cash_register:
(-)a/t/db_dependent/Koha/Booking.t (-220 / +91 lines)
Lines 20-26 Link Here
20
use Modern::Perl;
20
use Modern::Perl;
21
use utf8;
21
use utf8;
22
22
23
use Test::More tests => 6;
23
use Test::More tests => 2;
24
24
25
use Test::Exception;
25
use Test::Exception;
26
26
Lines 123-129 subtest 'Relation accessor tests' => sub { Link Here
123
};
123
};
124
124
125
subtest 'store() tests' => sub {
125
subtest 'store() tests' => sub {
126
    plan tests => 15;
126
    plan tests => 16;
127
    $schema->storage->txn_begin;
127
    $schema->storage->txn_begin;
128
128
129
    my $patron = $builder->build_object( { class => "Koha::Patrons" } );
129
    my $patron = $builder->build_object( { class => "Koha::Patrons" } );
Lines 323-337 subtest 'store() tests' => sub { Link Here
323
        # ✓ Any (1)         |--|
323
        # ✓ Any (1)         |--|
324
    };
324
    };
325
325
326
    subtest 'modification notice trigger' => sub {
326
    subtest 'confirmation notice trigger' => sub {
327
        plan tests => 3;
327
        plan tests => 2;
328
328
329
        my $original_notices_count = Koha::Notice::Messages->search(
329
        my $original_notices_count = Koha::Notice::Messages->search(
330
            {
331
                letter_code    => 'BOOKING_CONFIRMATION',
332
                borrowernumber => $patron->borrowernumber,
333
            }
334
        )->count;
335
336
        # Reuse previous booking to produce a clash
337
        eval { $booking = Koha::Booking->new( $booking->unblessed )->store };
338
339
        my $post_notices_count = Koha::Notice::Messages->search(
340
            {
341
                letter_code    => 'BOOKING_CONFIRMATION',
342
                borrowernumber => $patron->borrowernumber,
343
            }
344
        )->count;
345
        is(
346
            $post_notices_count,
347
            $original_notices_count,
348
            'Koha::Booking->store should not have enqueued a BOOKING_CONFIRMATION email if booking creation fails'
349
        );
350
351
        $start_1 = dt_from_string->add( months => 1 )->truncate( to => 'day' );
352
        $end_1   = $start_1->clone()->add( days => 1 );
353
354
        $booking = Koha::Booking->new(
355
            {
356
                patron_id         => $patron->borrowernumber,
357
                biblio_id         => $biblio->biblionumber,
358
                pickup_library_id => $item_2->homebranch,
359
                start_date        => $start_1->datetime(q{ }),
360
                end_date          => $end_1->datetime(q{ }),
361
            }
362
        )->store;
363
364
        $post_notices_count = Koha::Notice::Messages->search(
365
            {
366
                letter_code    => 'BOOKING_CONFIRMATION',
367
                borrowernumber => $patron->borrowernumber,
368
            }
369
        )->count;
370
        is(
371
            $post_notices_count,
372
            $original_notices_count + 1,
373
            'Koha::Booking->store should have enqueued a BOOKING_CONFIRMATION email for a new booking'
374
        );
375
    };
376
377
    subtest 'modification/cancellation notice triggers' => sub {
378
        plan tests => 5;
379
380
        my $original_modification_notices_count = Koha::Notice::Messages->search(
330
            {
381
            {
331
                letter_code    => 'BOOKING_MODIFICATION',
382
                letter_code    => 'BOOKING_MODIFICATION',
332
                borrowernumber => $patron->borrowernumber,
383
                borrowernumber => $patron->borrowernumber,
333
            }
384
            }
334
        )->count;
385
        )->count;
386
        my $original_cancellation_notices_count = Koha::Notice::Messages->search(
387
            {
388
                letter_code    => 'BOOKING_CANCELLATION',
389
                borrowernumber => $patron->borrowernumber,
390
            }
391
        )->count;
335
392
336
        $start_1 = dt_from_string->add( months => 1 )->truncate( to => 'day' );
393
        $start_1 = dt_from_string->add( months => 1 )->truncate( to => 'day' );
337
        $end_1   = $start_1->clone()->add( days => 1 );
394
        $end_1   = $start_1->clone()->add( days => 1 );
Lines 347-361 subtest 'store() tests' => sub { Link Here
347
            }
404
            }
348
        )->store;
405
        )->store;
349
406
350
        my $post_notices_count = Koha::Notice::Messages->search(
407
        my $post_modification_notices_count = Koha::Notice::Messages->search(
351
            {
408
            {
352
                letter_code    => 'BOOKING_MODIFICATION',
409
                letter_code    => 'BOOKING_MODIFICATION',
353
                borrowernumber => $patron->borrowernumber,
410
                borrowernumber => $patron->borrowernumber,
354
            }
411
            }
355
        )->count;
412
        )->count;
356
        is(
413
        is(
357
            $post_notices_count,
414
            $post_modification_notices_count,
358
            $original_notices_count,
415
            $original_modification_notices_count,
359
            'Koha::Booking->store should not have enqueued a BOOKING_MODIFICATION email for a new booking'
416
            'Koha::Booking->store should not have enqueued a BOOKING_MODIFICATION email for a new booking'
360
        );
417
        );
361
418
Lines 367-381 subtest 'store() tests' => sub { Link Here
367
            }
424
            }
368
        );
425
        );
369
426
370
        $post_notices_count = Koha::Notice::Messages->search(
427
        $post_modification_notices_count = Koha::Notice::Messages->search(
371
            {
428
            {
372
                letter_code    => 'BOOKING_MODIFICATION',
429
                letter_code    => 'BOOKING_MODIFICATION',
373
                borrowernumber => $patron->borrowernumber,
430
                borrowernumber => $patron->borrowernumber,
374
            }
431
            }
375
        )->count;
432
        )->count;
376
        is(
433
        is(
377
            $post_notices_count,
434
            $post_modification_notices_count,
378
            $original_notices_count,
435
            $original_modification_notices_count,
379
            'Koha::Booking->store should not have enqueued a BOOKING_MODIFICATION email for a booking with modified item_id'
436
            'Koha::Booking->store should not have enqueued a BOOKING_MODIFICATION email for a booking with modified item_id'
380
        );
437
        );
381
438
Lines 386-400 subtest 'store() tests' => sub { Link Here
386
        );
443
        );
387
444
388
        # start_date, end_date and pickup_library_id should behave identical
445
        # start_date, end_date and pickup_library_id should behave identical
389
        $post_notices_count = Koha::Notice::Messages->search(
446
        $post_modification_notices_count = Koha::Notice::Messages->search(
390
            {
447
            {
391
                letter_code    => 'BOOKING_MODIFICATION',
448
                letter_code    => 'BOOKING_MODIFICATION',
392
                borrowernumber => $patron->borrowernumber,
449
                borrowernumber => $patron->borrowernumber,
393
            }
450
            }
394
        )->count;
451
        )->count;
395
        is(
452
        is(
396
            $post_notices_count,
453
            $post_modification_notices_count,
397
            $original_notices_count + 1,
454
            $original_modification_notices_count + 1,
398
            'Koha::Booking->store should have enqueued a BOOKING_MODIFICATION email for a booking with modified start_date'
455
            'Koha::Booking->store should have enqueued a BOOKING_MODIFICATION email for a booking with modified start_date'
399
        );
456
        );
400
457
Lines 403-643 subtest 'store() tests' => sub { Link Here
403
                end_date => $end_1->clone()->add( days => 1 )->datetime(q{ }),
460
                end_date => $end_1->clone()->add( days => 1 )->datetime(q{ }),
404
            }
461
            }
405
        );
462
        );
406
    };
407
408
    subtest 'confirmation notice trigger' => sub {
409
        plan tests => 2;
410
463
411
        my $original_notices_count = Koha::Notice::Messages->search(
464
        $booking->update(
412
            {
465
            {
413
                letter_code    => 'BOOKING_CONFIRMATION',
466
                status => 'completed',
414
                borrowernumber => $patron->borrowernumber,
415
            }
467
            }
416
        )->count;
468
        );
417
418
        # Reuse previous booking to produce a clash
419
        eval { $booking = Koha::Booking->new( $booking->unblessed )->store };
420
469
421
        my $post_notices_count = Koha::Notice::Messages->search(
470
        my $post_cancellation_notices_count = Koha::Notice::Messages->search(
422
            {
471
            {
423
                letter_code    => 'BOOKING_CONFIRMATION',
472
                letter_code    => 'BOOKING_CANCELLATION',
424
                borrowernumber => $patron->borrowernumber,
473
                borrowernumber => $patron->borrowernumber,
425
            }
474
            }
426
        )->count;
475
        )->count;
427
        is(
476
        is(
428
            $post_notices_count,
477
            $post_cancellation_notices_count,
429
            $original_notices_count,
478
            $original_cancellation_notices_count,
430
            'Koha::Booking->store should not have enqueued a BOOKING_CONFIRMATION email if booking creation fails'
479
            'Koha::Booking->store should NOT have enqueued a BOOKING_CANCELLATION email for a booking status change that is not a "cancellation"'
431
        );
480
        );
432
481
433
        $start_1 = dt_from_string->add( months => 1 )->truncate( to => 'day' );
482
        $booking->update(
434
        $end_1   = $start_1->clone()->add( days => 1 );
435
436
        $booking = Koha::Booking->new(
437
            {
438
                patron_id         => $patron->borrowernumber,
439
                biblio_id         => $biblio->biblionumber,
440
                pickup_library_id => $item_2->homebranch,
441
                start_date        => $start_1->datetime(q{ }),
442
                end_date          => $end_1->datetime(q{ }),
443
            }
444
        )->store;
445
446
        $post_notices_count = Koha::Notice::Messages->search(
447
            {
483
            {
448
                letter_code    => 'BOOKING_CONFIRMATION',
484
                status => 'cancelled',
449
                borrowernumber => $patron->borrowernumber,
450
            }
485
            }
451
        )->count;
452
        is(
453
            $post_notices_count,
454
            $original_notices_count + 1,
455
            'Koha::Booking->store should have enqueued a BOOKING_CONFIRMATION email for a new booking'
456
        );
486
        );
457
    };
458
459
    $schema->storage->txn_rollback;
460
};
461
462
subtest 'delete() tests' => sub {
463
    plan tests => 2;
464
465
    $schema->storage->txn_begin;
466
467
    my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
468
    t::lib::Mocks::mock_userenv( { patron => $patron } );
469
    my $biblio                 = $builder->build_sample_biblio;
470
    my $item_1                 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } );
471
    my $start_0                = dt_from_string->subtract( days => 2 )->truncate( to => 'day' );
472
    my $end_0                  = $start_0->clone->add( days => 6 );
473
    my $original_notices_count = Koha::Notice::Messages->search(
474
        {
475
            letter_code    => 'BOOKING_CANCELLATION',
476
            borrowernumber => $patron->borrowernumber,
477
        }
478
    )->count;
479
480
    $item_1->bookable(1)->store;
481
482
    my $booking = Koha::Booking->new(
483
        {
484
            patron_id         => $patron->borrowernumber,
485
            biblio_id         => $biblio->biblionumber,
486
            item_id           => $item_1->itemnumber,
487
            pickup_library_id => $item_1->homebranch,
488
            start_date        => $start_0,
489
            end_date          => $end_0
490
        }
491
    )->store;
492
493
    my $deleted = $booking->delete;
494
    is(
495
        ref($deleted), 'Koha::Booking',
496
        'Koha::Booking->delete should return the Koha::Booking object if the booking has been correctly deleted'
497
    );
498
    is(
499
        Koha::Bookings->search( { booking_id => $booking->booking_id } )->count, 0,
500
        'Koha::Booking->delete should have deleted the booking'
501
    );
502
503
    $schema->storage->txn_rollback;
504
};
505
506
subtest 'edit() tests' => sub {
507
    plan tests => 1;
508
509
    $schema->storage->txn_begin;
510
511
    my $patron  = $builder->build_object( { class => 'Koha::Patrons' } );
512
    my $biblio  = $builder->build_sample_biblio;
513
    my $item_1  = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } );
514
    my $start_0 = dt_from_string->subtract( days => 2 )->truncate( to => 'day' );
515
    my $end_0   = $start_0->clone->add( days => 6 );
516
517
    $item_1->bookable(1)->store;
518
519
    my $booking = Koha::Booking->new(
520
        {
521
            patron_id         => $patron->borrowernumber,
522
            biblio_id         => $biblio->biblionumber,
523
            item_id           => $item_1->itemnumber,
524
            pickup_library_id => $item_1->homebranch,
525
            start_date        => $start_0,
526
            end_date          => $end_0,
527
        }
528
    )->store;
529
530
    my $booking_to_edit = Koha::Bookings->find( $booking->booking_id );
531
    $booking_to_edit->edit( { status => 'completed' } );
532
487
533
    is(
488
        $post_cancellation_notices_count = Koha::Notice::Messages->search(
534
        $booking_to_edit->unblessed->{status}, 'completed',
535
        'Koha::Booking->edit should edit booking with passed params'
536
    );
537
538
    $schema->storage->txn_rollback;
539
};
540
541
subtest 'cancel() tests' => sub {
542
    plan tests => 1;
543
544
    $schema->storage->txn_begin;
545
546
    my $patron                 = $builder->build_object( { class => 'Koha::Patrons' } );
547
    my $biblio                 = $builder->build_sample_biblio;
548
    my $item_1                 = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } );
549
    my $start_0                = dt_from_string->subtract( days => 2 )->truncate( to => 'day' );
550
    my $end_0                  = $start_0->clone->add( days => 6 );
551
    my $original_notices_count = Koha::Notice::Messages->search(
552
        {
553
            letter_code    => 'BOOKING_CANCELLATION',
554
            borrowernumber => $patron->borrowernumber,
555
        }
556
    )->count;
557
558
    $item_1->bookable(1)->store;
559
560
    my $booking = Koha::Booking->new(
561
        {
562
            patron_id         => $patron->borrowernumber,
563
            biblio_id         => $biblio->biblionumber,
564
            item_id           => $item_1->itemnumber,
565
            pickup_library_id => $item_1->homebranch,
566
            start_date        => $start_0,
567
            end_date          => $end_0,
568
        }
569
    )->store;
570
571
    my $booking_to_cancel = Koha::Bookings->find( $booking->booking_id );
572
    $booking_to_cancel->cancel( { send_letter => 1 } );
573
574
    subtest 'notice trigger' => sub {
575
        plan tests => 1;
576
577
        my $post_notices_count = Koha::Notice::Messages->search(
578
            {
489
            {
579
                letter_code    => 'BOOKING_CANCELLATION',
490
                letter_code    => 'BOOKING_CANCELLATION',
580
                borrowernumber => $patron->borrowernumber,
491
                borrowernumber => $patron->borrowernumber,
581
            }
492
            }
582
        )->count;
493
        )->count;
583
        is(
494
        is(
584
            $post_notices_count,
495
            $post_cancellation_notices_count,
585
            $original_notices_count + 1,
496
            $original_cancellation_notices_count + 1,
586
            'Koha::Booking->cancel should have enqueued a BOOKING_CANCELLATION email'
497
            'Koha::Booking->store should have enqueued a BOOKING_CANCELLATION email for a booking status change that is a "cancellation"'
587
        );
498
        );
588
    };
499
    };
589
500
590
    $schema->storage->txn_rollback;
501
    subtest 'status change exception' => sub {
591
};
592
593
subtest 'set_status() tests' => sub {
594
    plan tests => 3;
595
596
    $schema->storage->txn_begin;
597
598
    my $patron  = $builder->build_object( { class => 'Koha::Patrons' } );
599
    my $biblio  = $builder->build_sample_biblio;
600
    my $item_1  = $builder->build_sample_item( { biblionumber => $biblio->biblionumber } );
601
    my $start_0 = dt_from_string->subtract( days => 2 )->truncate( to => 'day' );
602
    my $end_0   = $start_0->clone->add( days => 6 );
603
604
    $item_1->bookable(1)->store;
605
606
    my $booking = Koha::Booking->new(
607
        {
608
            patron_id         => $patron->borrowernumber,
609
            biblio_id         => $biblio->biblionumber,
610
            item_id           => $item_1->itemnumber,
611
            pickup_library_id => $item_1->homebranch,
612
            start_date        => $start_0,
613
            end_date          => $end_0,
614
            status            => 'new',
615
        }
616
    )->store;
617
618
    my $booking_with_old_status = Koha::Bookings->find( $booking->booking_id );
619
    $booking_with_old_status->_set_status('completed');
620
    is( $booking_with_old_status->unblessed->{status}, 'completed', 'Booking status is now "completed"' );
621
622
    $booking_with_old_status->_set_status('cancelled');
623
    is( $booking_with_old_status->unblessed->{status}, 'cancelled', 'Booking status is now "cancelled"' );
624
625
    subtest 'unauthorized status' => sub {
626
        plan tests => 2;
502
        plan tests => 2;
627
503
628
        eval { $booking_with_old_status->_set_status('blah'); };
504
        $booking->discard_changes;
629
505
        my $status = $booking->status;
630
        if ($@) {
506
        throws_ok { $booking->update( { status => 'blah' } ) } 'Koha::Exceptions::Object::BadValue',
631
            like(
507
            'Throws exception when passed booking status would fail enum constraint';
632
                $@, qr/Invalid status: blah/,
633
                'An error is raised for unauthorized status'
634
            );
635
        } else {
636
            fail('Expected an error but none was raised');
637
        }
638
508
639
        # Status unchanged
509
        # Status unchanged
640
        is( $booking_with_old_status->unblessed->{status}, 'cancelled', 'Booking status is still "cancelled"' );
510
        $booking->discard_changes;
511
        is( $booking->status, $status, 'Booking status is unchanged' );
641
    };
512
    };
642
513
643
    $schema->storage->txn_rollback;
514
    $schema->storage->txn_rollback;
(-)a/t/db_dependent/api/v1/bookings.t (-4 / +2 lines)
Lines 477-485 subtest 'patch() tests' => sub { Link Here
477
    my $booking_id = $builder->build_object( { class => 'Koha::Bookings' } )->id;
477
    my $booking_id = $builder->build_object( { class => 'Koha::Bookings' } )->id;
478
478
479
    # Unauthorized attempt to partial update via PATCH
479
    # Unauthorized attempt to partial update via PATCH
480
    $t->patch_ok(
480
    $t->patch_ok( "//$unauth_userid:$password@/api/v1/bookings/$booking_id" => json => { status => 'cancelled' } )
481
        "//$unauth_userid:$password@/api/v1/bookings/$booking_id" => json => { status => 'cancelled' }
481
        ->status_is(403);
482
    )->status_is(403);
483
482
484
    my $biblio         = $builder->build_sample_biblio;
483
    my $biblio         = $builder->build_sample_biblio;
485
    my $item           = $builder->build_sample_item( { bookable => 1, biblionumber => $biblio->id } );
484
    my $item           = $builder->build_sample_item( { bookable => 1, biblionumber => $biblio->id } );
486
- 

Return to bug 38175