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

(-)a/C4/Circulation.pm (-31 / +36 lines)
Lines 1376-1400 sub CanBookBeIssued { Link Here
1376
    }
1376
    }
1377
1377
1378
    # CHECK FOR QUOTAS
1378
    # CHECK FOR QUOTAS
1379
    if ( my $quota = Koha::Patron::Quotas->get_patron_quota($patron->borrowernumber) ) {
1379
    if ( my $quotas = $patron->all_quotas->filter_by_active ) {
1380
        if (ref($quota) eq 'ARRAY') {
1380
        if ( $quotas->count > 1 ) {
1381
1381
            # Multiple available quotas found - need confirmation from user
1382
            # Multiple available quotas found - need confirmation from user
1382
            $needsconfirmation{QUOTA_SELECT} = $quota;
1383
            $needsconfirmation{QUOTA_SELECT} = $quotas;
1383
        }
1384
        } elsif ( $quotas->count == 1 ) {
1384
        elsif (!$quota->has_available_quota) {
1385
            my $quota = $quotas->next;
1385
            if ( C4::Context->preference("AllowQuotaOverride") ) {
1386
            if ( !$quota->has_available_quota ) {
1386
                $needsconfirmation{QUOTA_EXCEEDED} = {
1387
                if ( C4::Context->preference("AllowQuotaOverride") ) {
1387
                    available => $quota->available_quota,
1388
                    $needsconfirmation{QUOTA_EXCEEDED} = {
1388
                    total => $quota->allocation, 
1389
                        available => $quota->available_quota,
1389
                    used => $quota->used,
1390
                        total     => $quota->allocation,
1390
                };
1391
                        used      => $quota->used,
1391
            }
1392
                    };
1392
            else {
1393
                } else {
1393
                $issuingimpossible{QUOTA_EXCEEDED} = {
1394
                    $issuingimpossible{QUOTA_EXCEEDED} = {
1394
                    available => $quota->available_quota,
1395
                        available => $quota->available_quota,
1395
                    total => $quota->allocation, 
1396
                        total     => $quota->allocation,
1396
                    used => $quota->used,
1397
                        used      => $quota->used,
1397
                };
1398
                    };
1399
                }
1398
            }
1400
            }
1399
        }
1401
        }
1400
    }
1402
    }
Lines 2033-2039 sub AddIssue { Link Here
2033
            if ($selected_quota_id) {
2035
            if ($selected_quota_id) {
2034
                $quota = Koha::Patron::Quotas->find($selected_quota_id);
2036
                $quota = Koha::Patron::Quotas->find($selected_quota_id);
2035
            } else {
2037
            } else {
2036
                $quota = Koha::Patron::Quotas->get_patron_quota($patron->borrowernumber); 
2038
                $quota = $patron->all_quotas->filter_by_active->single;
2037
            }
2039
            }
2038
2040
2039
            if ($quota) {
2041
            if ($quota) {
Lines 3417-3430 sub CanBookBeRenewed { Link Here
3417
    }
3419
    }
3418
3420
3419
    # # CHECK FOR QUOTAS  
3421
    # # CHECK FOR QUOTAS  
3420
    if (my $quota_usage = Koha::Patron::Quota::Usages->find({ issue_id => $issue->issue_id })) {
3422
    if ( my $quota_usage = Koha::Patron::Quota::Usages->find( { issue_id => $issue->issue_id } ) ) {
3421
        
3423
3422
        # Get current active quota for the patron
3424
        # Get current actives quota for the patron
3423
        if (my $active_quota = Koha::Patron::Quotas->get_active_quota($quota_usage->patron_id)) {
3425
        if ( my $active_quotas = $patron->all_quotas->filter_by_active ) {
3424
            
3426
            my $all_exceeded = 1;
3425
            if (!$active_quota->has_available_quota) {
3427
            while ( my $active_quota = $active_quotas->next ) {
3426
                return (0, "QUOTA_EXCEEDED");
3428
                if ( $active_quota->has_available_quota ) {
3429
                    $all_exceeded = 0;
3430
                }
3427
            }
3431
            }
3432
            return ( 0, "QUOTA_EXCEEDED" ) if $all_exceeded;
3428
        }
3433
        }
3429
    }
3434
    }
3430
3435
Lines 3545-3556 sub AddRenewal { Link Here
3545
    # Check quotas and record usage if needed
3550
    # Check quotas and record usage if needed
3546
    if (my $quota_usage = Koha::Patron::Quota::Usages->find({ issue_id => $issue->issue_id })) {
3551
    if (my $quota_usage = Koha::Patron::Quota::Usages->find({ issue_id => $issue->issue_id })) {
3547
        # Get current active quota for the patron
3552
        # Get current active quota for the patron
3548
        if (my $active_quota = Koha::Patron::Quotas->get_active_quota($quota_usage->patron_id)) {
3553
        if (my $active_quota = Koha::Patron::Quotas->find($quota_usage->patron_quota_id)) {
3549
            # Update patron's used quota value
3554
            # Update patron's used quota value
3550
            $active_quota->add_usage({
3555
                $active_quota->add_usage({
3551
                patron_id => $patron->patron_id,
3556
                    patron_id => $patron->borrowernumber,
3552
                issue_id  => $issue->issue_id,
3557
                    issue_id  => $issue->issue_id,
3553
            });
3558
                });
3554
        }
3559
        }
3555
    }
3560
    }
3556
3561
(-)a/Koha/Patron.pm (+63 lines)
Lines 584-589 sub is_guarantor { Link Here
584
    return $self->guarantee_relationships()->count();
584
    return $self->guarantee_relationships()->count();
585
}
585
}
586
586
587
=head3 quotas
588
589
Returns any quotas this patron may have
590
591
=cut
592
593
sub quotas {
594
    my ($self) = @_;
595
    my $quotas_rs = $self->_result->patron_quotas;
596
    return Koha::Patron::Quotas->_new_from_dbic($quotas_rs);
597
}
598
599
=head3 guarantors_quotas
600
601
Returns any guarantor quotas this patron may use
602
603
=cut
604
605
sub guarantors_quotas {
606
    my ($self) = @_;
607
    my $guarantors_query =
608
        $self->_result->borrower_relationships_guarantors->search( {}, { columns => ['guarantor_id'] } )->as_query;
609
    return Koha::Patron::Quotas->search(
610
        {
611
            patron_id => { '-in' => $guarantors_query },
612
        }
613
    );
614
}
615
616
=head3 all_quotas
617
618
Returns a Koha::Patron::Quotas resultset containing:
619
- Directly related quotas (first)
620
- Guarantor quotas (second, if applicable)
621
622
=cut
623
624
sub all_quotas {
625
    my ($self) = @_;
626
627
    # Step 1: Build a subquery to fetch the patron's own ID + guarantor IDs from borrower_relationships
628
    my $guarantors_query =
629
        $self->_result->borrower_relationships_guarantees->search( {}, { columns => ['guarantor_id'] } )->as_query;
630
631
    # Step 2: Fetch quotas where borrowernumber is either:
632
    # - The patron's own borrowernumber
633
    # - One of the guarantor_id values found in the subquery
634
    return Koha::Patron::Quotas->search(
635
        {
636
            '-or' => [
637
                { patron_id => $self->id },                         # The patron's own quotas
638
                { patron_id => { '-in' => $guarantors_query } },    # Guarantor quotas if they exist
639
            ],
640
        },
641
        {
642
            order_by => \[
643
                "CASE WHEN patron_id = ? THEN 0 ELSE 1 END, patron_id",
644
                $self->id
645
            ],
646
        }
647
    );
648
}
649
587
=head3 relationships_debt
650
=head3 relationships_debt
588
651
589
Returns the amount owed by the patron's guarantors *and* the other guarantees of those guarantors
652
Returns the amount owed by the patron's guarantors *and* the other guarantees of those guarantors
(-)a/Koha/Patron/Quotas.pm (-101 / +7 lines)
Lines 13-83 Koha::Patron::Quotas - Koha Patron Quota Object set class Link Here
13
13
14
=head2 Class Methods
14
=head2 Class Methods
15
15
16
=head3 get_patron_quota
17
18
    my $quota = Koha::Patron::Quotas->get_patron_quota($patron_id);
19
20
Searches for any applicable quota for the given patron. Returns:
21
- First available quota found for patron or their guarantor (if UseGuarantorQuota enabled)
22
- If no available quota found, returns the first found quota
23
- Returns undef if no quota found
24
25
=cut
26
27
sub get_patron_quota {
28
    my ( $self, $patron_id ) = @_;
29
30
    my @available_quotas;
31
    my $first_found_quota = undef;
32
33
    # First check patron's own quota
34
    my $patron_quota = $self->search(
35
        {
36
            patron_id  => $patron_id,
37
            start_date => { '<=' => \'CURRENT_DATE' },
38
            end_date   => { '>=' => \'CURRENT_DATE' }
39
        }
40
    )->single;
41
42
    if ( $patron_quota && $patron_quota->has_available_quota ) {
43
        push @available_quotas, $patron_quota;
44
    }
45
    $first_found_quota ||= $patron_quota if $patron_quota;
46
47
    # Then check all guarantor quotas if enabled
48
    if ( C4::Context->preference('UseGuarantorQuota') ) {
49
        my $patron     = Koha::Patrons->find($patron_id);
50
        my @guarantors = $patron->guarantor_relationships->guarantors->as_list;
51
52
        foreach my $guarantor (@guarantors) {
53
            my $guarantor_quota = $self->search(
54
                {
55
                    patron_id  => $guarantor->borrowernumber,
56
                    start_date => { '<=' => \'CURRENT_DATE' },
57
                    end_date   => { '>=' => \'CURRENT_DATE' }
58
                }
59
            )->single;
60
            
61
            if ($guarantor_quota) {
62
63
                # Store first found quota in case we need it later
64
                $first_found_quota ||= $guarantor_quota;
65
66
                # Collect available guarantor quotas
67
                if ( $guarantor_quota->has_available_quota ) {
68
                    push @available_quotas, $guarantor_quota;
69
                }
70
            }
71
        }
72
    }
73
74
    # Return single quota if only one available, array of quotas if multiple available,
75
    # first found quota if none available
76
    return $available_quotas[0] if @available_quotas == 1;
77
    return \@available_quotas   if @available_quotas > 1;
78
    return $first_found_quota;
79
}
80
81
=head3 create_quota
16
=head3 create_quota
82
17
83
Creates a new quota record for a patron
18
Creates a new quota record for a patron
Lines 90-144 sub create_quota { Link Here
90
    return Koha::Patron::Quota->new($params)->store;
25
    return Koha::Patron::Quota->new($params)->store;
91
}
26
}
92
27
93
=head3 search_by_patron
28
=head3 filter_by_active
94
29
95
Finds all quotas for a patron
30
    $quotas->filter_by_active;
96
31
97
=cut
32
Returns the currently active quotas for the resultset.
98
33
99
sub search_by_patron {
34
Active means start_date <= NOW() and end_date >= NOW().
100
    my ( $self, $patron_id ) = @_;
101
    return $self->search( { patron_id => $patron_id } );
102
}
103
104
=head3 get_active_quotas
105
106
Returns multiple active quota records
107
35
108
=cut
36
=cut
109
37
110
sub get_active_quotas {
38
sub filter_by_active {
111
    my ($self) = @_;
39
    my ($self) = @_;
112
    return $self->search(
40
    return $self->search(
113
        {
41
        {
114
            start_date => { '<=' => \'CURRENT_DATE' },
42
            start_date => { '<=' => \'NOW()' },
115
            end_date   => { '>=' => \'CURRENT_DATE' }
43
            end_date   => { '>=' => \'NOW()' },
116
        }
44
        }
117
    );
45
    );
118
}
46
}
119
47
120
=head2 get_active_quota
121
122
  my $active_quota = Koha::Patron::Quotas->get_active_quota($patron_id);
123
124
Returns the currently active quota for a patron if one exists.
125
Active means start_date <= NOW() and end_date >= NOW().
126
Returns undef if no active quota found.
127
128
=cut
129
130
sub get_active_quota {
131
    my ( $self, $patron_id ) = @_;
132
133
    return Koha::Patron::Quotas->search(
134
        {
135
            patron_id  => $patron_id,
136
            start_date => { '<=' => \'CURRENT_DATE' },
137
            end_date   => { '>=' => \'CURRENT_DATE' }
138
        }
139
    )->single;
140
}
141
142
=head2 Internal methods
48
=head2 Internal methods
143
49
144
=head3 _type
50
=head3 _type
(-)a/Koha/REST/V1/Patrons/Quotas.pm (-1 / +1 lines)
Lines 34-40 sub list { Link Here
34
        }
34
        }
35
35
36
        if ($only_active) {
36
        if ($only_active) {
37
            $quotas_set = $quotas_set->get_active_quotas;
37
            $quotas_set = $quotas_set->filter_by_active;
38
        }
38
        }
39
39
40
        return $c->render(
40
        return $c->render(
(-)a/circ/circulation.pl (-1 / +1 lines)
Lines 321-326 if ($patron) { Link Here
321
    {
321
    {
322
        $template->param( limited_category => 1 );
322
        $template->param( limited_category => 1 );
323
    }
323
    }
324
324
}
325
}
325
326
326
#
327
#
327
- 

Return to bug 38924