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

(-)a/Koha/CirculationRules.pm (-4 lines)
Lines 194-203 our $RULE_KINDS = { Link Here
194
        scope        => [ 'branchcode', 'categorycode', 'itemtype' ],
194
        scope        => [ 'branchcode', 'categorycode', 'itemtype' ],
195
        can_be_blank => 0,
195
        can_be_blank => 0,
196
    },
196
    },
197
    overdue_X_has_rules => {
198
        scope        => [ 'branchcode', 'categorycode', 'itemtype' ],
199
        can_be_blank => 0,
200
    },
201
    renewalperiod => {
197
    renewalperiod => {
202
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
198
        scope => [ 'branchcode', 'categorycode', 'itemtype' ],
203
    },
199
    },
(-)a/installer/data/mysql/atomicupdate/bug_10190.pl (-4 lines)
Lines 113-122 return { Link Here
113
                    $insert->execute( $branchcode, $categorycode, $itemtype, "overdue_${i}_restrict", $restrict );
113
                    $insert->execute( $branchcode, $categorycode, $itemtype, "overdue_${i}_restrict", $restrict );
114
                }
114
                }
115
115
116
                # has_rules is a UI sentinel that marks an explicit ruleset for this context/trigger.
117
                # It is not used by the backend (overdue_notices.pl).
118
                $insert->execute( $branchcode, $categorycode, $itemtype, "overdue_${i}_has_rules", 1 );
119
120
                $migrated++;
116
                $migrated++;
121
            }
117
            }
122
        }
118
        }
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/CirculationTriggersFormAdd.vue (-2 lines)
Lines 599-606 export default { Link Here
599
                    );
599
                    );
600
            }
600
            }
601
601
602
            ruleSetToSubmit[`overdue_${this.triggerNumber}_has_rules`] = true;
603
604
            // in edit mode, check for changes from elsewhere to the rule set being edited
602
            // in edit mode, check for changes from elsewhere to the rule set being edited
605
            if (this.editMode === "edit") {
603
            if (this.editMode === "edit") {
606
                const ruleSetInDb = await this.getSelectedRuleSet(this.context);
604
                const ruleSetInDb = await this.getSelectedRuleSet(this.context);
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/CirculationTriggersFormConfirmTriggerDelete.vue (-3 / +11 lines)
Lines 49-56 import { storeToRefs } from "pinia"; Link Here
49
export default {
49
export default {
50
    setup() {
50
    setup() {
51
        const circRulesStore = inject("circRulesStore");
51
        const circRulesStore = inject("circRulesStore");
52
        const { handleContext, findEffectiveRule, deleteRuleSet } =
52
        const {
53
            circRulesStore;
53
            handleContext,
54
            findEffectiveRule,
55
            deleteRuleSet,
56
            hasExplicitRulesForTrigger,
57
        } = circRulesStore;
54
        const { libraries, allCurrentLibraryRawRuleSets, ruleSuffixes } =
58
        const { libraries, allCurrentLibraryRawRuleSets, ruleSuffixes } =
55
            storeToRefs(circRulesStore);
59
            storeToRefs(circRulesStore);
56
        return {
60
        return {
Lines 60-65 export default { Link Here
60
            allCurrentLibraryRawRuleSets,
64
            allCurrentLibraryRawRuleSets,
61
            ruleSuffixes,
65
            ruleSuffixes,
62
            deleteRuleSet,
66
            deleteRuleSet,
67
            hasExplicitRulesForTrigger,
63
        };
68
        };
64
    },
69
    },
65
    data() {
70
    data() {
Lines 108-114 export default { Link Here
108
                };
113
                };
109
114
110
                if (
115
                if (
111
                    ruleSet[`overdue_${this.triggerNumber}_has_rules`] === null
116
                    !this.hasExplicitRulesForTrigger(
117
                        ruleSet,
118
                        this.triggerNumber
119
                    )
112
                ) {
120
                ) {
113
                    return;
121
                    return;
114
                }
122
                }
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/stores/circulation-rules.js (-27 / +41 lines)
Lines 32-38 export const useCircRulesStore = defineStore("circRules", () => { Link Here
32
            { code: "sms", name: "SMS" },
32
            { code: "sms", name: "SMS" },
33
            { code: "print", name: "Print" },
33
            { code: "print", name: "Print" },
34
        ],
34
        ],
35
        regex: /overdue_(\d+)_has_rules/,
36
        // rule sets
35
        // rule sets
37
        allDefaultLibraryRawRuleSets: [], // source of truth for default library
36
        allDefaultLibraryRawRuleSets: [], // source of truth for default library
38
        allCurrentLibraryRawRuleSets: [], // source of truth for current library
37
        allCurrentLibraryRawRuleSets: [], // source of truth for current library
Lines 97-106 export const useCircRulesStore = defineStore("circRules", () => { Link Here
97
            }
96
            }
98
            return value.includes(type) ? $__("Yes") : $__("No");
97
            return value.includes(type) ? $__("Yes") : $__("No");
99
        },
98
        },
99
        hasExplicitRulesForTrigger(ruleSet, triggerNumber) {
100
            return ["delay", "notice", "mtt", "restrict"].some(
101
                suffix => ruleSet[`overdue_${triggerNumber}_${suffix}`] != null
102
            );
103
        },
100
        hasConflict(oldRuleSet, newRuleSet, triggerNumber) {
104
        hasConflict(oldRuleSet, newRuleSet, triggerNumber) {
101
            if (
105
            if (
102
                !oldRuleSet ||
106
                !oldRuleSet ||
103
                oldRuleSet[`overdue_${triggerNumber}_has_rules`] === null
107
                !this.hasExplicitRulesForTrigger(oldRuleSet, triggerNumber)
104
            ) {
108
            ) {
105
                return false;
109
                return false;
106
            }
110
            }
Lines 125-132 export const useCircRulesStore = defineStore("circRules", () => { Link Here
125
        },
129
        },
126
        isOnlyRuleSetForTrigger(triggerNumber) {
130
        isOnlyRuleSetForTrigger(triggerNumber) {
127
            return (
131
            return (
128
                this.allCurrentLibraryRawRuleSets.filter(
132
                this.allCurrentLibraryRawRuleSets.filter(ruleSet =>
129
                    ruleSet => ruleSet[`overdue_${triggerNumber}_has_rules`]
133
                    this.hasExplicitRulesForTrigger(ruleSet, triggerNumber)
130
                ).length === 1
134
                ).length === 1
131
            );
135
            );
132
        },
136
        },
Lines 163-174 export const useCircRulesStore = defineStore("circRules", () => { Link Here
163
                    ruleSet?.context.item_type_id === context.item_type_id
167
                    ruleSet?.context.item_type_id === context.item_type_id
164
            );
168
            );
165
169
166
            // if handling 'has_rules', stop here
170
            // if handling 'has_rules', derive from actual rules rather than DB
167
            if (ruleSuffix === "has_rules") {
171
            if (ruleSuffix === "has_rules") {
172
                const hasExplicit = this.currentAndDefaultRawRuleSets.some(
173
                    ruleSet =>
174
                        ruleSet?.context.library_id === context.library_id &&
175
                        ruleSet?.context.patron_category_id ===
176
                            context.patron_category_id &&
177
                        ruleSet?.context.item_type_id ===
178
                            context.item_type_id &&
179
                        this.hasExplicitRulesForTrigger(ruleSet, triggerNumber)
180
                );
168
                return {
181
                return {
169
                    value: existingRule?.[`overdue_${triggerNumber}_has_rules`],
182
                    value: hasExplicit ? true : null,
170
                    isFallback:
183
                    isFallback: !hasExplicit,
171
                        !existingRule?.[`overdue_${triggerNumber}_has_rules`],
172
                };
184
                };
173
            }
185
            }
174
186
Lines 178-185 export const useCircRulesStore = defineStore("circRules", () => { Link Here
178
                    value: existingRule[
190
                    value: existingRule[
179
                        `overdue_${triggerNumber}_${ruleSuffix}`
191
                        `overdue_${triggerNumber}_${ruleSuffix}`
180
                    ],
192
                    ],
181
                    isFallback:
193
                    isFallback: !this.hasExplicitRulesForTrigger(
182
                        !existingRule[`overdue_${triggerNumber}_has_rules`],
194
                        existingRule,
195
                        triggerNumber
196
                    ),
183
                };
197
                };
184
            }
198
            }
185
199
Lines 289-295 export const useCircRulesStore = defineStore("circRules", () => { Link Here
289
                    i <= this.triggerCounts[this.currentLibraryId];
303
                    i <= this.triggerCounts[this.currentLibraryId];
290
                    i++
304
                    i++
291
                ) {
305
                ) {
292
                    if (ruleSet[`overdue_${i}_has_rules`] === null) {
306
                    if (!this.hasExplicitRulesForTrigger(ruleSet, i)) {
293
                        continue;
307
                        continue;
294
                    }
308
                    }
295
                    this.ruleSuffixes.forEach(ruleSuffix => {
309
                    this.ruleSuffixes.forEach(ruleSuffix => {
Lines 326-343 export const useCircRulesStore = defineStore("circRules", () => { Link Here
326
            // - Rule sets exists that override default triggers. Therefore, their triggerCount is the same as default's.
340
            // - Rule sets exists that override default triggers. Therefore, their triggerCount is the same as default's.
327
            // - Rule sets exists for triggers for which there is no default.
341
            // - Rule sets exists for triggers for which there is no default.
328
            //     => such triggers are follow up addition to the existing default sequence.
342
            //     => such triggers are follow up addition to the existing default sequence.
329
            //     => the triggerCount for this library will be higher than default's, and equal to the highest trigger number for this library for which has_rules is not null.
343
            //     => the triggerCount for this library will be higher than default's, and equal to the highest trigger number for this library that has any explicit rules.
330
344
331
            const ruleNames = Object.keys(this.allDefaultLibraryRawRuleSets[0]);
332
            // Set the triggerCount for the default library rule set
345
            // Set the triggerCount for the default library rule set
333
            if (this.currentLibraryId === "*") {
346
            if (this.currentLibraryId === "*") {
334
                this.triggerCounts["*"] = ruleNames.filter(
347
                const triggerNumRegex =
335
                    ruleSuffix =>
348
                    /^overdue_(\d+)_(delay|notice|mtt|restrict)$/;
336
                        this.regex.test(ruleSuffix) &&
349
                const triggerNums = new Set();
337
                        this.allDefaultLibraryRawRuleSets.some(
350
                this.allDefaultLibraryRawRuleSets.forEach(ruleSet => {
338
                            ruleSet => ruleSet[ruleSuffix] !== null
351
                    Object.keys(ruleSet).forEach(key => {
339
                        )
352
                        const match = key.match(triggerNumRegex);
340
                ).length;
353
                        if (match && ruleSet[key] !== null) {
354
                            triggerNums.add(parseInt(match[1]));
355
                        }
356
                    });
357
                });
358
                this.triggerCounts["*"] =
359
                    triggerNums.size > 0 ? Math.max(...triggerNums) : 0;
341
                return;
360
                return;
342
            }
361
            }
343
362
Lines 351-359 export const useCircRulesStore = defineStore("circRules", () => { Link Here
351
            // Set a library-specific trigger count: at least one rule set exists -> start from the first trigger for which there is no default rule set
370
            // Set a library-specific trigger count: at least one rule set exists -> start from the first trigger for which there is no default rule set
352
            let i = this.triggerCounts["*"] + 1;
371
            let i = this.triggerCounts["*"] + 1;
353
            while (
372
            while (
354
                ruleNames.includes(`overdue_${i}_has_rules`) &&
373
                this.allCurrentLibraryRawRuleSets.some(ruleSet =>
355
                this.allCurrentLibraryRawRuleSets.some(
374
                    this.hasExplicitRulesForTrigger(ruleSet, i)
356
                    ruleSet => ruleSet[`overdue_${i}_has_rules`] !== null
357
                )
375
                )
358
            ) {
376
            ) {
359
                i++;
377
                i++;
Lines 391-397 export const useCircRulesStore = defineStore("circRules", () => { Link Here
391
            if (ruleSet[`overdue_${triggerNumber}_mtt`] !== null) {
409
            if (ruleSet[`overdue_${triggerNumber}_mtt`] !== null) {
392
                rulesForDeletion[`overdue_${triggerNumber}_mtt`] = null;
410
                rulesForDeletion[`overdue_${triggerNumber}_mtt`] = null;
393
            }
411
            }
394
            rulesForDeletion[`overdue_${triggerNumber}_has_rules`] = null;
395
            this.updateCircRuleSets(rulesForDeletion, triggerNumber);
412
            this.updateCircRuleSets(rulesForDeletion, triggerNumber);
396
        },
413
        },
397
        async getAllRawRuleSets() {
414
        async getAllRawRuleSets() {
Lines 473-480 export const useCircRulesStore = defineStore("circRules", () => { Link Here
473
                existingRuleSet[`overdue_${triggerNumber}_restrict`];
490
                existingRuleSet[`overdue_${triggerNumber}_restrict`];
474
            circRuleSet[`overdue_${triggerNumber}_mtt`] =
491
            circRuleSet[`overdue_${triggerNumber}_mtt`] =
475
                existingRuleSet[`overdue_${triggerNumber}_mtt`];
492
                existingRuleSet[`overdue_${triggerNumber}_mtt`];
476
            circRuleSet[`overdue_${triggerNumber}_has_rules`] =
477
                existingRuleSet[`overdue_${triggerNumber}_has_rules`];
478
            const client = APIClient.circRule;
493
            const client = APIClient.circRule;
479
            await client.circ_rules.update(circRuleSet);
494
            await client.circ_rules.update(circRuleSet);
480
        },
495
        },
481
- 

Return to bug 10190