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

(-)a/Koha/CirculationRules.pm (-3 / +26 lines)
Lines 222-227 our $RULE_KINDS = { Link Here
222
    bookings_trail_period => {
222
    bookings_trail_period => {
223
        scope => [ 'branchcode', 'itemtype' ],
223
        scope => [ 'branchcode', 'itemtype' ],
224
    },
224
    },
225
    has_priority => {
226
        scope => [ 'branchcode', 'categorycode' ],
227
    },
225
    # Not included (deprecated?):
228
    # Not included (deprecated?):
226
    #   * accountsent
229
    #   * accountsent
227
    #   * reservecharge
230
    #   * reservecharge
Lines 254-259 sub get_effective_rule { Link Here
254
    $params->{categorycode} //= undef;
257
    $params->{categorycode} //= undef;
255
    $params->{branchcode}   //= undef;
258
    $params->{branchcode}   //= undef;
256
    $params->{itemtype}     //= undef;
259
    $params->{itemtype}     //= undef;
260
    $params->{has_priority} //= undef;
257
261
258
    my $rule_name    = $params->{rule_name};
262
    my $rule_name    = $params->{rule_name};
259
    my $categorycode = $params->{categorycode};
263
    my $categorycode = $params->{categorycode};
Lines 286-292 sub get_effective_rule { Link Here
286
        }
290
        }
287
    )->single;
291
    )->single;
288
292
289
    return $rule;
293
    $search_params->{has_priority} = 1;
294
295
    my $priority_rule = $self->search(
296
        $search_params,
297
        {
298
            order_by => $order_by,
299
            rows => 1,
300
        }
301
    )->single;
302
303
    return defined $priority_rule ? $priority_rule : $rule;
290
}
304
}
291
305
292
=head3 get_effective_rule_value
306
=head3 get_effective_rule_value
Lines 314-323 sub get_effective_rule_value { Link Here
314
    my $categorycode = $params->{categorycode};
328
    my $categorycode = $params->{categorycode};
315
    my $itemtype     = $params->{itemtype};
329
    my $itemtype     = $params->{itemtype};
316
    my $branchcode   = $params->{branchcode};
330
    my $branchcode   = $params->{branchcode};
331
    my $has_priority = $params->{has_priority};
317
332
318
    my $memory_cache = Koha::Cache::Memory::Lite->get_instance;
333
    my $memory_cache = Koha::Cache::Memory::Lite->get_instance;
319
    my $cache_key = sprintf "CircRules:%s:%s:%s:%s", $rule_name // q{},
334
    my $cache_key = sprintf "CircRules:%s:%s:%s:%s", $rule_name // q{},
320
      $categorycode // q{}, $branchcode // q{}, $itemtype // q{};
335
      $categorycode // q{}, $branchcode // q{}, $itemtype // q{} ,$has_priority // q{};
321
336
322
    my $cached       = $memory_cache->get_from_cache($cache_key);
337
    my $cached       = $memory_cache->get_from_cache($cache_key);
323
    return $cached if $cached;
338
    return $cached if $cached;
Lines 340-345 sub get_effective_rules { Link Here
340
    my $categorycode = $params->{categorycode};
355
    my $categorycode = $params->{categorycode};
341
    my $itemtype     = $params->{itemtype};
356
    my $itemtype     = $params->{itemtype};
342
    my $branchcode   = $params->{branchcode};
357
    my $branchcode   = $params->{branchcode};
358
    my $has_priority = $params->{has_priority};
343
359
344
    my $r;
360
    my $r;
345
    foreach my $rule (@$rules) {
361
    foreach my $rule (@$rules) {
Lines 349-354 sub get_effective_rules { Link Here
349
                categorycode => $categorycode,
365
                categorycode => $categorycode,
350
                itemtype     => $itemtype,
366
                itemtype     => $itemtype,
351
                branchcode   => $branchcode,
367
                branchcode   => $branchcode,
368
                has_priority => $has_priority,
352
            }
369
            }
353
        );
370
        );
354
371
Lines 392-397 sub set_rule { Link Here
392
    my $itemtype     = $params->{itemtype};
409
    my $itemtype     = $params->{itemtype};
393
    my $rule_name    = $params->{rule_name};
410
    my $rule_name    = $params->{rule_name};
394
    my $rule_value   = $params->{rule_value};
411
    my $rule_value   = $params->{rule_value};
412
    my $has_priority = $params->{has_priority};
413
395
    my $can_be_blank = defined $kind_info->{can_be_blank} ? $kind_info->{can_be_blank} : 1;
414
    my $can_be_blank = defined $kind_info->{can_be_blank} ? $kind_info->{can_be_blank} : 1;
396
    $rule_value = undef if defined $rule_value && $rule_value eq "" && !$can_be_blank;
415
    $rule_value = undef if defined $rule_value && $rule_value eq "" && !$can_be_blank;
397
    my $is_monetary = defined $kind_info->{is_monetary} ? $kind_info->{is_monetary} : 0;
416
    my $is_monetary = defined $kind_info->{is_monetary} ? $kind_info->{is_monetary} : 0;
Lines 407-412 sub set_rule { Link Here
407
            branchcode   => $branchcode,
426
            branchcode   => $branchcode,
408
            categorycode => $categorycode,
427
            categorycode => $categorycode,
409
            itemtype     => $itemtype,
428
            itemtype     => $itemtype,
429
            has_priority => $has_priority
410
        }
430
        }
411
    )->next();
431
    )->next();
412
432
Lines 428-433 sub set_rule { Link Here
428
                    itemtype     => $itemtype,
448
                    itemtype     => $itemtype,
429
                    rule_name    => $rule_name,
449
                    rule_name    => $rule_name,
430
                    rule_value   => $rule_value,
450
                    rule_value   => $rule_value,
451
                    has_priority => $has_priority,
431
                }
452
                }
432
            );
453
            );
433
            $rule->store();
454
            $rule->store();
Lines 453-459 sub set_rules { Link Here
453
    $set_params{branchcode} = $params->{branchcode} if exists $params->{branchcode};
474
    $set_params{branchcode} = $params->{branchcode} if exists $params->{branchcode};
454
    $set_params{categorycode} = $params->{categorycode} if exists $params->{categorycode};
475
    $set_params{categorycode} = $params->{categorycode} if exists $params->{categorycode};
455
    $set_params{itemtype} = $params->{itemtype} if exists $params->{itemtype};
476
    $set_params{itemtype} = $params->{itemtype} if exists $params->{itemtype};
456
    my $rules        = $params->{rules};
477
    $set_params{has_priority} = $params->{has_priority} if exists $params->{has_priority};
478
    my $rules = $params->{rules};
479
457
480
458
    my $rule_objects = [];
481
    my $rule_objects = [];
459
    while ( my ( $rule_name, $rule_value ) = each %$rules ) {
482
    while ( my ( $rule_name, $rule_value ) = each %$rules ) {
(-)a/Koha/Schema/Result/CirculationRule.pm (-2 / +9 lines)
Lines 62-67 __PACKAGE__->table("circulation_rules"); Link Here
62
  is_nullable: 0
62
  is_nullable: 0
63
  size: 32
63
  size: 32
64
64
65
=head2 has_priority
66
67
  data_type: 'tinyint'
68
  is_nullable: 1
69
65
=cut
70
=cut
66
71
67
__PACKAGE__->add_columns(
72
__PACKAGE__->add_columns(
Lines 77-82 __PACKAGE__->add_columns( Link Here
77
  { data_type => "varchar", is_nullable => 0, size => 32 },
82
  { data_type => "varchar", is_nullable => 0, size => 32 },
78
  "rule_value",
83
  "rule_value",
79
  { data_type => "varchar", is_nullable => 0, size => 32 },
84
  { data_type => "varchar", is_nullable => 0, size => 32 },
85
  "has_priority",
86
  { data_type => "tinyint", is_nullable => 1 },
80
);
87
);
81
88
82
=head1 PRIMARY KEY
89
=head1 PRIMARY KEY
Lines 177-184 __PACKAGE__->belongs_to( Link Here
177
);
184
);
178
185
179
186
180
# Created by DBIx::Class::Schema::Loader v0.07042 @ 2018-10-05 14:29:17
187
# Created by DBIx::Class::Schema::Loader v0.07051 @ 2024-12-20 12:51:01
181
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:QHMqvrtX0ohJe70PHUYZ0Q
188
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:P5S9o9p6aJ38EOkLQQOW0Q
182
189
183
sub koha_objects_class {
190
sub koha_objects_class {
184
    'Koha::CirculationRules';
191
    'Koha::CirculationRules';
(-)a/Koha/Template/Plugin/CirculationRules.pm (+2 lines)
Lines 76-81 sub Search { Link Here
76
    $branchcode   = undef if $branchcode eq q{}   or $branchcode eq q{*};
76
    $branchcode   = undef if $branchcode eq q{}   or $branchcode eq q{*};
77
    $categorycode = undef if $categorycode eq q{} or $categorycode eq q{*};
77
    $categorycode = undef if $categorycode eq q{} or $categorycode eq q{*};
78
    $itemtype     = undef if $itemtype eq q{}     or $itemtype eq q{*};
78
    $itemtype     = undef if $itemtype eq q{}     or $itemtype eq q{*};
79
    my $has_priority = $params->{has_priority} // undef;
79
80
80
    my $rule = Koha::CirculationRules->search(
81
    my $rule = Koha::CirculationRules->search(
81
        {
82
        {
Lines 83-88 sub Search { Link Here
83
            categorycode => $categorycode,
84
            categorycode => $categorycode,
84
            itemtype     => $itemtype,
85
            itemtype     => $itemtype,
85
            rule_name    => $rule_name,
86
            rule_name    => $rule_name,
87
            has_priority => $has_priority
86
        }
88
        }
87
    )->next;
89
    )->next;
88
90
(-)a/admin/smart-rules.pl (-1 / +39 lines)
Lines 749-754 elsif ( $op eq 'cud-mod-refund-lost-item-fee-rule' ) { Link Here
749
            rules        => { waiting_hold_cancellation => undef },
749
            rules        => { waiting_hold_cancellation => undef },
750
        }
750
        }
751
    );
751
    );
752
} elsif ( $op eq 'cud-set-global-checkout-limits' ) {
753
754
    my $categorycode  = $input->param('categorycode');
755
    my $itemtype  = $input->param('itemtype');
756
    my $maxissueqty = scalar $input->param('maxissueqty');
757
    my $maxonsiteissueqty = scalar $input->param('maxonsiteissueqty');
758
759
    my %checkout_rules;
760
    $checkout_rules{'maxissueqty'} = strip_non_numeric($maxissueqty);
761
    $checkout_rules{'maxonsiteissueqty'} = strip_non_numeric($maxonsiteissueqty);
762
763
    while (my ($rule_name, $rule_value) = each(%checkout_rules)) {
764
        if(defined $rule_value && $rule_value ne '') {
765
            Koha::CirculationRules->set_rule(
766
            {   categorycode => ( $categorycode eq '*' ) ? undef : $categorycode,
767
                itemtype => ( $itemtype eq '*' ) ? undef : $itemtype,
768
                branchcode   => undef,
769
                has_priority => 1,
770
                rule_name        => $rule_name,
771
                rule_value => $rule_value
772
            });
773
        }
774
    }
775
} elsif ( $op eq 'cud-delete-global-checkout-limits' ) {
776
    my $categorycode  = $input->param('categorycode');
777
    my $itemtype  = $input->param('itemtype');
778
779
    Koha::CirculationRules->set_rules(
780
        {   categorycode => ( $categorycode eq '*' ) ? undef : $categorycode,
781
            itemtype => ( $itemtype eq '*' ) ? undef : $itemtype,
782
            branchcode   => undef,
783
            has_priority => 1,
784
            rules        => {
785
                maxissueqty       => undef,
786
                maxonsiteissueqty => undef,
787
            },
788
        }
789
    );
752
}
790
}
753
791
754
792
Lines 776-782 my @used_itemtypes = Link Here
776
    Koha::CirculationRules->search( { branchcode => $humanbranch }, { columns => ['itemtype'], distinct => 1, } )
814
    Koha::CirculationRules->search( { branchcode => $humanbranch }, { columns => ['itemtype'], distinct => 1, } )
777
    ->get_column('itemtype');
815
    ->get_column('itemtype');
778
816
779
my $all_rules = Koha::CirculationRules->search({ branchcode => $humanbranch });
817
my $all_rules = Koha::CirculationRules->search({ branchcode => $humanbranch, has_priority => undef });
780
my $definedbranch = $all_rules->count ? 1 : 0;
818
my $definedbranch = $all_rules->count ? 1 : 0;
781
819
782
my $rules = {};
820
my $rules = {};
(-)a/installer/data/mysql/atomicupdate/Bug_8137-add-column-has_priority.pl (+15 lines)
Line 0 Link Here
1
use Modern::Perl;
2
3
return {
4
    bug_number  => "8137",
5
    description => "Add column has_priority",
6
    up          => sub {
7
        my ($args) = @_;
8
        my ( $dbh, $out ) = @$args{qw(dbh out)};
9
10
        if ( !column_exists( 'circulation_rules', 'has_priority' ) ) {
11
            $dbh->do('ALTER TABLE circulation_rules ADD IF NOT EXISTS has_priority TINYINT(1) DEFAULT NULL');
12
            say $out "Added column 'circulation_rules.has_priority'";
13
        }
14
    },
15
};
(-)a/installer/data/mysql/kohastructure.sql (+1 lines)
Lines 1876-1881 CREATE TABLE `circulation_rules` ( Link Here
1876
  `itemtype` varchar(10) DEFAULT NULL,
1876
  `itemtype` varchar(10) DEFAULT NULL,
1877
  `rule_name` varchar(32) NOT NULL,
1877
  `rule_name` varchar(32) NOT NULL,
1878
  `rule_value` varchar(32) NOT NULL,
1878
  `rule_value` varchar(32) NOT NULL,
1879
  `has_priority` tinyint(1) DEFAULT NULL,
1879
  PRIMARY KEY (`id`),
1880
  PRIMARY KEY (`id`),
1880
  UNIQUE KEY `branchcode` (`branchcode`,`categorycode`,`itemtype`,`rule_name`),
1881
  UNIQUE KEY `branchcode` (`branchcode`,`categorycode`,`itemtype`,`rule_name`),
1881
  KEY `circ_rules_ibfk_2` (`categorycode`),
1882
  KEY `circ_rules_ibfk_2` (`categorycode`),
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/smart-rules.tt (-1 / +124 lines)
Lines 71-76 Link Here
71
                    or the specific rule's type, whichever is less.</p>
71
                    or the specific rule's type, whichever is less.</p>
72
                    <p>To modify a rule, create a new one with the same patron category and item type.</p>
72
                    <p>To modify a rule, create a new one with the same patron category and item type.</p>
73
                    <p>The circulation and fine rules are applied based on the CircControl system preference which is set to <a href="/cgi-bin/koha/admin/preferences.pl?op=search&searchfield=CircControl">[% Koha.Preference('CircControl') | html %]</a> and the HomeOrHoldingBranch system preference which is set to <a href="/cgi-bin/koha/admin/preferences.pl?op=search&searchfield=HomeOrHoldingBranch">[% Koha.Preference('HomeOrHoldingBranch') | html %]</a>.</p>
73
                    <p>The circulation and fine rules are applied based on the CircControl system preference which is set to <a href="/cgi-bin/koha/admin/preferences.pl?op=search&searchfield=CircControl">[% Koha.Preference('CircControl') | html %]</a> and the HomeOrHoldingBranch system preference which is set to <a href="/cgi-bin/koha/admin/preferences.pl?op=search&searchfield=HomeOrHoldingBranch">[% Koha.Preference('HomeOrHoldingBranch') | html %]</a>.</p>
74
                    <p>The “Global checkout limits” section is designed to create a limit for all librairies, thus preventing checkouts accumulation.</p>
74
                </div>
75
                </div>
75
76
76
                <div class="page-section">
77
                <div class="page-section">
Lines 900-905 Link Here
900
                </div>
901
                </div>
901
                [% END %]
902
                [% END %]
902
903
904
                <div id="global_checkout_limits" class="page-section">
905
                    <h2>Global checkout limits</h2>
906
                    <p>Specify checkout limits for all librairies.</p>
907
                    <form id="set-global-checkouts-limit" method="post" action="/cgi-bin/koha/admin/smart-rules.pl">
908
                        [% INCLUDE 'csrf-token.inc' %]
909
                        <input type="hidden" name="op" value="cud-set-global-checkout-limits" />
910
                        <table>
911
                            <thead>
912
                                <tr>
913
                                    <th class="fixed_sort">Patron category</th>
914
                                    <th class="fixed_sort">Item type</th>
915
                                    <th class="noExport restricted-access">Actions</th>
916
                                    <th>Current checkouts allowed</th>
917
                                    <th>Current on-site checkouts allowed</th>
918
                                </tr>
919
                            </thead>
920
                            <tbody>
921
                            [% FOREACH c IN categorycodes %]
922
                                [% SET c = '' UNLESS c.defined %]
923
                                [% FOREACH i IN itemtypes %]
924
                                    [% SET i = '' UNLESS i.defined %]
925
                                    [% SET maxissueqty = CirculationRules.Search( '', c, i, 'maxissueqty', { want_rule => 1, has_priority => 1 } ) %]
926
                                    [% SET maxonsiteissueqty = CirculationRules.Search( '', c, i, 'maxonsiteissueqty', { want_rule => 1, has_priority => 1 } ) %]
927
                                    [% SET show_rule = maxissueqty || maxonsiteissueqty %]
928
                                    [% IF show_rule %]
929
                                        <tr>
930
                                            <td data-code="[% c | html %]">
931
                                                [% IF c == undef %]
932
                                                    <em>All</em>
933
                                                [% ELSE %]
934
                                                    [% Categories.GetName(c) | html %]
935
                                                [% END %]
936
                                            </td>
937
                                            <td data-code="[% i | html %]">
938
                                                [% IF i == undef %]
939
                                                    <em>All</em>
940
                                                [% ELSE %]
941
                                                    [% ItemTypes.GetDescription(i,1) | html %]
942
                                                [% END %]
943
                                            </td>
944
                                            <td class="actions restricted-access">
945
                                                <a href="#" class="editrule btn btn-default btn-xs"><i class="fa-solid fa-pencil" aria-hidden="true"></i> Edit</a>
946
                                                <a href="#" class="delete-global-checkout-limits btn btn-default btn-xs" data-itemtype="[% i || '*' | html %]" data-categorycode="[% c || '*' | html %]" data-branch="*"><i class="fa fa-trash-can"></i> Delete</a>
947
                                            </td>
948
                                            <td>
949
                                                [% IF maxissueqty.rule_value && maxissueqty.rule_value != '' %]
950
                                                    [% maxissueqty.rule_value | html %]
951
                                                [% ELSE %]
952
                                                    <span>Not defined</span>
953
                                                [% END %]
954
                                            </td>
955
                                            <td>
956
                                                [% IF maxonsiteissueqty.rule_value && maxonsiteissueqty.rule_value != ''  %]
957
                                                    [% maxonsiteissueqty.rule_value | html %]
958
                                                [% ELSE %]
959
                                                    <span>Not defined</span>
960
                                                [% END %]
961
                                            </td>
962
                                        </tr>
963
                                    [% END %]
964
                                [% END %]
965
                            [% END %]
966
                            <tr class="noExport restricted-access" id="edit_row">
967
                                <td>
968
                                    <select name="categorycode" id="categorycode">
969
                                        <option value="*">All</option>
970
                                    [% FOREACH patron_category IN patron_categories%]
971
                                        <option value="[% patron_category.categorycode | html %]">[% patron_category.description | html %]</option>
972
                                    [% END %]
973
                                    </select>
974
                                </td>
975
                                <td>
976
                                    <select name="itemtype" id="matrixitemtype" style="width:13em;">
977
                                        <option value="*">All</option>
978
                                        [% FOREACH itemtypeloo IN itemtypeloop %]
979
                                            [% NEXT IF itemtypeloo.parent_type %]
980
                                            [% SET children = itemtypeloo.children_with_localization %]
981
                                            [% IF children.count %]
982
                                                <optgroup label="[% itemtypeloo.translated_description | html %]">
983
                                                    <option value="[% itemtypeloo.itemtype | html %]">[% itemtypeloo.translated_description | html %] (All)</option>
984
                                                    [% FOREACH child IN children %]
985
                                                        <option value="[% child.itemtype | html %]">[% child.translated_description | html %]</option>
986
                                                    [% END %]
987
                                                </optgroup>
988
                                            [% ELSE %]
989
                                                <option value="[% itemtypeloo.itemtype | html %]">[% itemtypeloo.translated_description | html %]</option>
990
                                            [% END %]
991
                                        [% END %]
992
                                    </select>
993
                                </td>
994
                                <td class="actions">
995
                                    <button type="submit" class="btn btn-primary btn-xs"><i class="fa fa-save"></i> Save</button>
996
                                    <button name="cancel" class="clear_edit btn btn-default btn-xs"><i class="fa fa-undo"></i> Clear</button>
997
                                </td>
998
                                <td><input type="text" name="maxissueqty" id="maxissueqty" size="3" /></td>
999
                                <td><input type="text" name="maxonsiteissueqty" id="maxonsiteissueqty" size="3" /></td>
1000
                            </tr>
1001
                            </tbody>
1002
                        </table>
1003
                    </form>
1004
                </div>
1005
903
                <div id="waiting-hold-cancel-category" class="page-section">
1006
                <div id="waiting-hold-cancel-category" class="page-section">
904
                [% IF humanbranch %]
1007
                [% IF humanbranch %]
905
                    <h2>Waiting hold cancellation policy for [% Branches.GetName( humanbranch ) | html %]</h2>
1008
                    <h2>Waiting hold cancellation policy for [% Branches.GetName( humanbranch ) | html %]</h2>
Lines 1711-1716 Link Here
1711
                return f.submit();
1814
                return f.submit();
1712
            });
1815
            });
1713
1816
1817
            $(".delete-global-checkout-limits").on("click", function(e){
1818
                e.preventDefault();
1819
                if ( !confirmDelete(MSG_CONFIRM_DELETE) ) {
1820
                    return false;
1821
                }
1822
                let f = $("#delete_form");
1823
                f.find("[name='op']").val('cud-delete-global-checkout-limits');
1824
                f.find("[name='itemtype']").val($(this).data('itemtype'));
1825
                f.find("[name='categorycode']").val($(this).data('categorycode'));
1826
                f.find("[name='branch']").val($(this).data('branch'));
1827
                return f.submit();
1828
            });
1829
1830
            if($("#branch").val() != '*') {
1831
                var rowCount = $("#default-circulation-rules tbody tr").length;
1832
                if(rowCount == 1){
1833
                    $("#global_checkout_limits").remove();
1834
                } else {
1835
	                $("#global_checkout_limits .restricted-access").remove();
1836
                }
1837
	        }
1714
        });
1838
        });
1715
    </script>
1839
    </script>
1716
[% END %]
1840
[% END %]
1717
- 

Return to bug 8137