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

(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/CirculationTriggersFormAdd.vue (-137 / +225 lines)
Lines 12-24 Link Here
12
                <div class="page-section bg-info" v-if="circRules.length">
12
                <div class="page-section bg-info" v-if="circRules.length">
13
                    <h2>{{ $__("Trigger context") }}</h2>
13
                    <h2>{{ $__("Trigger context") }}</h2>
14
                    <TriggerContext :ruleInfo="ruleInfo" />
14
                    <TriggerContext :ruleInfo="ruleInfo" />
15
                    <h2>{{ $__("Existing rules") }}</h2>
15
                    <h2 v-if="ruleInfo.numberOfTriggers > 0">{{ $__("Existing rules") }}</h2>
16
                    <p>{{ $__("Notice") }} {{ " " + newTriggerNumber - 1 }}</p>
16
                    <p v-if="ruleInfo.numberOfTriggers > 0">{{ $__("Notice") }} {{ " " + newTriggerNumber - 1 }}</p>
17
                    <TriggersTable
17
                    <TriggersTable
18
                        v-if="ruleInfo.numberOfTriggers > 0"
18
                        :circRules="circRules"
19
                        :circRules="circRules"
19
                        :triggerNumber="newTriggerNumber - 1"
20
                        :triggerNumber="newTriggerNumber - 1"
20
                        :modal="true"
21
                        :modal="true"
21
                        :ruleBeingEdited="ruleBeingEdited"
22
                        :ruleBeingEdited="ruleBeingEdited"
23
                        :triggerBeingEdited="triggerBeingEdited"
22
                        :letters="letters"
24
                        :letters="letters"
23
                    />
25
                    />
24
                </div>
26
                </div>
Lines 30-36 Link Here
30
                            >
32
                            >
31
                            <v-select
33
                            <v-select
32
                                id="library_id"
34
                                id="library_id"
33
                                v-model="circRuleTrigger.library_id"
35
                                v-model="newRule.library_id"
34
                                label="name"
36
                                label="name"
35
                                :reduce="lib => lib.library_id"
37
                                :reduce="lib => lib.library_id"
36
                                :options="libraries"
38
                                :options="libraries"
Lines 39-45 Link Here
39
                            >
41
                            >
40
                                <template #search="{ attributes, events }">
42
                                <template #search="{ attributes, events }">
41
                                    <input
43
                                    <input
42
                                        :required="!circRuleTrigger.library_id"
44
                                        :required="!newRule.library_id"
43
                                        class="vs__search"
45
                                        class="vs__search"
44
                                        v-bind="attributes"
46
                                        v-bind="attributes"
45
                                        v-on="events"
47
                                        v-on="events"
Lines 54-60 Link Here
54
                            >
56
                            >
55
                            <v-select
57
                            <v-select
56
                                id="patron_category_id"
58
                                id="patron_category_id"
57
                                v-model="circRuleTrigger.patron_category_id"
59
                                v-model="newRule.patron_category_id"
58
                                label="name"
60
                                label="name"
59
                                :reduce="cat => cat.patron_category_id"
61
                                :reduce="cat => cat.patron_category_id"
60
                                :options="categories"
62
                                :options="categories"
Lines 64-70 Link Here
64
                                <template #search="{ attributes, events }">
66
                                <template #search="{ attributes, events }">
65
                                    <input
67
                                    <input
66
                                        :required="
68
                                        :required="
67
                                            !circRuleTrigger.patron_category_id
69
                                            !newRule.patron_category_id
68
                                        "
70
                                        "
69
                                        class="vs__search"
71
                                        class="vs__search"
70
                                        v-bind="attributes"
72
                                        v-bind="attributes"
Lines 80-86 Link Here
80
                            >
82
                            >
81
                            <v-select
83
                            <v-select
82
                                id="item_type_id"
84
                                id="item_type_id"
83
                                v-model="circRuleTrigger.item_type_id"
85
                                v-model="newRule.item_type_id"
84
                                label="description"
86
                                label="description"
85
                                :reduce="type => type.item_type_id"
87
                                :reduce="type => type.item_type_id"
86
                                :options="itemTypes"
88
                                :options="itemTypes"
Lines 90-96 Link Here
90
                                <template #search="{ attributes, events }">
92
                                <template #search="{ attributes, events }">
91
                                    <input
93
                                    <input
92
                                        :required="
94
                                        :required="
93
                                            !circRuleTrigger.item_type_id
95
                                            !newRule.item_type_id
94
                                        "
96
                                        "
95
                                        class="vs__search"
97
                                        class="vs__search"
96
                                        v-bind="attributes"
98
                                        v-bind="attributes"
Lines 106-113 Link Here
106
                            </label>
108
                            </label>
107
                            <input
109
                            <input
108
                                id="overdue_delay"
110
                                id="overdue_delay"
109
                                v-model="circRuleTrigger.delay"
111
                                v-model="newRule.delay"
110
                                type="number"
112
                                type="number"
113
                                :placeholder="fallbackRule.delay"
114
                                :min="minDelay"
111
                            />
115
                            />
112
                        </li>
116
                        </li>
113
                        <li>
117
                        <li>
Lines 116-122 Link Here
116
                            >
120
                            >
117
                            <v-select
121
                            <v-select
118
                                id="letter_code"
122
                                id="letter_code"
119
                                v-model="circRuleTrigger.notice"
123
                                v-model="newRule.notice"
120
                                label="name"
124
                                label="name"
121
                                :reduce="type => type.code"
125
                                :reduce="type => type.code"
122
                                :options="letters"
126
                                :options="letters"
Lines 126-142 Link Here
126
                                        class="vs__search"
130
                                        class="vs__search"
127
                                        v-bind="attributes"
131
                                        v-bind="attributes"
128
                                        v-on="events"
132
                                        v-on="events"
133
                                        :placeholder="letters.find(letter => letter.code === fallbackRule.notice)?.name || fallbackRule.notice"
129
                                    />
134
                                    />
130
                                </template>
135
                                </template>
131
                            </v-select>
136
                            </v-select>
132
                        </li>
137
                        </li>
133
                        <li>
138
                        <li v-if="newRule.notice !== '' || (( newRule.notice === null || newRule.notice === undefined ) && fallbackRule.notice !== '' )">
134
                            <label for="mtt"
139
                            <label for="mtt"
135
                                >{{ $__("Transport type(s)") }}:</label
140
                                >{{ $__("Transport type(s)") }}:</label
136
                            >
141
                            >
137
                            <v-select
142
                            <v-select
138
                                id="mtt"
143
                                id="mtt"
139
                                v-model="circRuleTrigger.mtt"
144
                                v-model="newRule.mtt"
140
                                label="name"
145
                                label="name"
141
                                :reduce="type => type.code"
146
                                :reduce="type => type.code"
142
                                :options="mtts"
147
                                :options="mtts"
Lines 147-168 Link Here
147
                                        class="vs__search"
152
                                        class="vs__search"
148
                                        v-bind="attributes"
153
                                        v-bind="attributes"
149
                                        v-on="events"
154
                                        v-on="events"
155
                                        :placeholder="newRule.mtt === null || newRule.mtt === undefined || newRule.mtt.length === 0 ? fallbackRule.mtt : ''"
150
                                    />
156
                                    />
151
                                </template>
157
                                </template>
152
                            </v-select>
158
                            </v-select>
153
                        </li>
159
                        </li>
154
                        <li>
160
                        <li>
155
                            <label for="restricts"
161
                            <label for="restricts">{{ $__("Restricts checkouts") }}:</label>
156
                                >{{ $__("Restricts checkouts") }}:</label
162
                            <div>
157
                            >
163
                                <input
158
                            <input
164
                                    type="radio"
159
                                type="checkbox"
165
                                    id="restricts-yes"
160
                                id="restricts"
166
                                    v-model="newRule.restrict"
161
                                :checked="false"
167
                                    :value="1"
162
                                true-value="1"
168
                                />
163
                                false-value="0"
169
                                {{ $__("Yes") }}
164
                                v-model="circRuleTrigger.restrict"
170
                    
165
                            />
171
                                <input
172
                                    type="radio"
173
                                    id="restricts-no"
174
                                    v-model="newRule.restrict"
175
                                    :value="0"
176
                                />
177
                                {{ $__("No") }}
178
                    
179
                                <input
180
                                    type="radio"
181
                                    id="restricts-fallback"
182
                                    v-model="newRule.restrict"
183
                                    :value="null"
184
                                />
185
                                {{ $__("Fallback to default") }}
186
                                <span v-if="fallbackRule.restricts !== null">
187
                                    ({{ fallbackRule.restricts === 1 ? $__("Yes") : $__("No") }})
188
                                </span>
189
                            </div>
166
                        </li>
190
                        </li>
167
                    </ol>
191
                    </ol>
168
                </fieldset>
192
                </fieldset>
Lines 209-231 export default { Link Here
209
            categories: null,
233
            categories: null,
210
            itemTypes: null,
234
            itemTypes: null,
211
            circRules: [],
235
            circRules: [],
212
            circRuleTrigger: {
236
            newRule: {
213
                item_type_id: "*",
237
                item_type_id: "*",
214
                library_id: "*",
238
                library_id: "*",
215
                patron_category_id: "*",
239
                patron_category_id: "*",
216
                delay: null,
240
                delay: null,
217
                notice: null,
241
                notice: null,
218
                mtt: null,
242
                mtt: null,
219
                restrict: "0",
243
                restrict: null,
220
            },
244
            },
221
            effectiveRule: {
245
            fallbackRule: {
222
                item_type_id: "*",
246
                item_type_id: "*",
223
                library_id: "*",
247
                library_id: "*",
224
                patron_category_id: "*",
248
                patron_category_id: "*",
225
                delay: null,
249
                delay: null,
226
                notice: null,
250
                notice: null,
227
                mtt: null,
251
                mtt: null,
228
                restrict: "0",
252
                restrict: null,
229
            },
253
            },
230
            newTriggerNumber: 1,
254
            newTriggerNumber: 1,
231
            mtts: [
255
            mtts: [
Lines 239-306 export default { Link Here
239
                fine: null,
263
                fine: null,
240
                chargeperiod: null,
264
                chargeperiod: null,
241
                lengthunit: null,
265
                lengthunit: null,
266
                numberOfTriggers: null,
242
            },
267
            },
243
            editMode: false,
268
            editMode: false,
244
            ruleBeingEdited: null,
269
            ruleBeingEdited: null,
270
            triggerBeingEdited: null,
245
        }
271
        }
246
    },
272
    },
247
    beforeRouteEnter(to, from, next) {
273
    beforeRouteEnter(to, from, next) {
248
        next(vm => {
274
        next(vm => {
249
            vm.getLibraries().then(() =>
275
            vm.getLibraries().then(() =>
250
                vm.getCategories().then(() =>
276
                vm.getCategories().then(() =>
251
                    vm.getItemTypes().then(() => {
277
                    vm.getItemTypes().then(() =>
252
                        const { query } = to
278
                        vm.getCircRules().then(() => {
253
                        vm.checkForExistingRules(query).then(
279
                            const { query } = to
254
                            () => (vm.initialized = true)
280
                            vm.checkForExistingRules(query).then(
255
                        )
281
                                () => (vm.initialized = true)
256
                    })
282
                            )
283
                        })
284
                    )
257
                )
285
                )
258
            )
286
            )
259
        })
287
        })
260
    },
288
    },
289
    computed: {
290
        minDelay() {
291
            const lastRule = this.circRules[this.newTriggerNumber - 2];
292
            return lastRule ? parseInt(lastRule[`overdue_${this.newTriggerNumber - 1}_delay`]) + 1 : 0;
293
        }
294
    },
261
    methods: {
295
    methods: {
262
        async addCircRule(e) {
296
        async addCircRule(e) {
263
            e.preventDefault()
297
            e.preventDefault()
264
298
265
            const context = {
299
            const context = {
266
                library_id: this.circRuleTrigger.library_id || "*",
300
                library_id: this.newRule.library_id || "*",
267
                item_type_id: this.circRuleTrigger.item_type_id || "*",
301
                item_type_id: this.newRule.item_type_id || "*",
268
                patron_category_id:
302
                patron_category_id:
269
                    this.circRuleTrigger.patron_category_id || "*",
303
                    this.newRule.patron_category_id || "*",
270
            }
304
            }
271
305
272
            const delay =
273
                this.circRuleTrigger.delay === this.effectiveRule.delay
274
                    ? null
275
                    : this.circRuleTrigger.delay
276
            const notice =
277
                this.circRuleTrigger.notice === this.effectiveRule.notice
278
                    ? null
279
                    : this.circRuleTrigger.notice
280
            const restrict =
281
                this.circRuleTrigger.restrict === this.effectiveRule.restrict
282
                    ? null
283
                    : this.circRuleTrigger.restrict
284
            const mtt =
285
                this.circRuleTrigger.mtt.join(",") ===
286
                this.effectiveRule.mtt.join(",")
287
                    ? null
288
                    : this.circRuleTrigger.mtt.join(",")
289
290
            const circRule = {
306
            const circRule = {
291
                context,
307
                context,
292
            }
308
            }
293
            circRule[`overdue_${this.newTriggerNumber}_delay`] = delay
309
            circRule[`overdue_${this.newTriggerNumber}_delay`] = this.newRule.delay
294
            circRule[`overdue_${this.newTriggerNumber}_notice`] = notice
310
            circRule[`overdue_${this.newTriggerNumber}_notice`] = this.newRule.notice
295
            circRule[`overdue_${this.newTriggerNumber}_restrict`] = restrict
311
            circRule[`overdue_${this.newTriggerNumber}_restrict`] = this.newRule.restrict
296
            circRule[`overdue_${this.newTriggerNumber}_mtt`] = mtt
312
            circRule[`overdue_${this.newTriggerNumber}_mtt`] = this.newRule.mtt && this.newRule.mtt.length ? this.newRule.mtt.join(",") : null
297
298
            if (this.editMode) {
299
                Object.keys(circRule).forEach(key => {
300
                    if (key === "context") return
301
                    if (!circRule[key]) delete circRule[key]
302
                })
303
            }
304
313
305
            const client = APIClient.circRule
314
            const client = APIClient.circRule
306
            await client.circRules.update(circRule).then(
315
            await client.circRules.update(circRule).then(
Lines 354-359 export default { Link Here
354
                error => {}
363
                error => {}
355
            )
364
            )
356
        },
365
        },
366
        async getCircRules() {
367
            const client = APIClient.circRule
368
            await client.circRules.getAll({}, { effective: false }).then(
369
                rules => {
370
                    const { rulesPerTrigger } = this.splitCircRulesByTriggerNumber(rules)
371
                    this.circRules = rulesPerTrigger.length ? rulesPerTrigger : rules
372
                },
373
                error => {}
374
            )               
375
        },
357
        async handleContextChange() {
376
        async handleContextChange() {
358
            await this.checkForExistingRules()
377
            await this.checkForExistingRules()
359
        },
378
        },
Lines 362-456 export default { Link Here
362
            const editMode = routeParams && routeParams.triggerNumber
381
            const editMode = routeParams && routeParams.triggerNumber
363
            if (editMode) {
382
            if (editMode) {
364
                this.editMode = editMode
383
                this.editMode = editMode
365
                this.ruleBeingEdited = routeParams.triggerNumber
384
                this.triggerBeingEdited = routeParams.triggerNumber
366
            }
385
            }
367
            const library_id =
386
            const library_id =
368
                routeParams && routeParams.library_id
387
                routeParams && routeParams.library_id
369
                    ? routeParams.library_id
388
                    ? routeParams.library_id
370
                    : this.circRuleTrigger.library_id || "*"
389
                    : this.newRule.library_id || "*"
371
            const item_type_id =
390
            const item_type_id =
372
                routeParams && routeParams.item_type_id
391
                routeParams && routeParams.item_type_id
373
                    ? routeParams.item_type_id
392
                    ? routeParams.item_type_id
374
                    : this.circRuleTrigger.item_type_id || "*"
393
                    : this.newRule.item_type_id || "*"
375
            const patron_category_id =
394
            const patron_category_id =
376
                routeParams && routeParams.patron_category_id
395
                routeParams && routeParams.patron_category_id
377
                    ? routeParams.patron_category_id
396
                    ? routeParams.patron_category_id
378
                    : this.circRuleTrigger.patron_category_id || "*"
397
                    : this.newRule.patron_category_id || "*"
379
            const params = {
398
            const params = {
380
                library_id,
399
                library_id,
381
                item_type_id,
400
                item_type_id,
382
                patron_category_id,
401
                patron_category_id,
383
            }
402
            }
384
403
404
            // Fetch effective ruleset for context
385
            const client = APIClient.circRule
405
            const client = APIClient.circRule
386
            await client.circRules.getAll({}, params).then(
406
            await client.circRules.getAll({}, params).then(
387
                rules => {
407
                rules => {
388
                    if (rules.length === 0) {
408
                    this.ruleBeingEdited = rules[0]
389
                        this.newTriggerNumber = 1
409
                    this.ruleBeingEdited.context = params
390
                    } else {
410
                    const regex = /overdue_(\d+)_delay/g
391
                        const { rulesPerTrigger } =
411
                    const numberOfTriggers = Object.keys(rules[0]).filter(
392
                            this.splitCircRulesByTriggerNumber(rules)
412
                        key => regex.test(key) && rules[0][key] !== null
393
                        this.circRules = rulesPerTrigger
413
                    ).length
394
                        this.ruleInfo = {
414
                    const splitRules = this.filterCircRulesByContext(this.ruleBeingEdited)
395
                            issuelength: rules[0].issuelength,
415
                    this.newTriggerNumber = editMode
396
                            decreaseloanholds: rules[0].decreaseloanholds,
416
                        ? routeParams.triggerNumber
397
                            fine: rules[0].fine,
417
                        : numberOfTriggers + 1
398
                            chargeperiod: rules[0].chargeperiod,
418
                    this.assignTriggerValues(
399
                            lengthunit: rules[0].lengthunit,
419
                        splitRules,
400
                        }
420
                        this.newTriggerNumber,
421
                        params
422
                    )
423
424
                    this.ruleInfo = {
425
                        issuelength: rules[0].issuelength,
426
                        decreaseloanholds: rules[0].decreaseloanholds,
427
                        fine: rules[0].fine,
428
                        chargeperiod: rules[0].chargeperiod,
429
                        lengthunit: rules[0].lengthunit,
430
                        numberOfTriggers: numberOfTriggers,
401
                    }
431
                    }
402
                },
432
                },
403
                error => {}
433
                error => { }
404
            )
434
            )
435
        },
436
        filterCircRulesByContext(effectiveRule) {
437
            const context = effectiveRule.context;
438
        
439
            // Filter rules that match the context
440
            let contextRules = this.circRules.filter(rule => {
441
                return Object.keys(context).every(key => {
442
                    return context[key] === rule.context[key];
443
                });
444
            });
445
        
446
            // Calculate the number of 'overdue_X_' triggers in the effectiveRule
447
            const regex = /overdue_(\d+)_delay/g;
448
            const numberOfTriggers = Object.keys(effectiveRule).filter(
449
                key => regex.test(key) && effectiveRule[key] !== null
450
            ).length;
451
        
452
            // Ensure there is one contextRule per 'X' from 1 to numberOfTriggers
453
            for (let i = 1; i <= numberOfTriggers; i++) {
454
                // Check if there's already a rule for overdue_X_ in contextRules
455
                const matchingRule = contextRules.find(rule => rule[`overdue_${i}_delay`] !== undefined);
456
        
457
                if (!matchingRule) {
458
                    // Create a new rule with the same context and null overdue_X_* keys
459
                    const placeholderRule = {
460
                        context: { ...context }, // Clone the context
461
                        [`overdue_${i}_delay`]: null,
462
                        [`overdue_${i}_notice`]: null,
463
                        [`overdue_${i}_mtt`]: null,
464
                        [`overdue_${i}_restrict`]: null
465
                    };
466
        
467
                    // Add the new rule to contextRules
468
                    contextRules.push(placeholderRule);
469
                }
470
            }
405
471
406
            params.effective = false
472
            // Sort contextRules by the 'X' value in 'overdue_X_delay'
407
            await client.circRules.getAll({}, params).then(
473
            contextRules.sort((a, b) => {
408
                rules => {
474
                const getX = rule => {
409
                    if (rules.length === 0) {
475
                    const match = Object.keys(rule).find(key => regex.test(key));
410
                        this.newTriggerNumber = 1
476
                    return match ? parseInt(match.match(/\d+/)[0], 10) : 0;
477
                };
478
        
479
                return getX(a) - getX(b);
480
            });
411
481
412
                        this.assignTriggerValues(
482
            return contextRules;
413
                            "circRuleTrigger",
483
        },
414
                            this.circRules,
484
        findFallbackRule(currentContext, key) {
415
                            this.newTriggerNumber,
485
416
                            params
486
            // Filter rules to only those with non-null values for the specified key and not the current context
417
                        )
487
            const relevantRules = this.circRules.filter(
418
                        this.assignTriggerValues(
488
                rule => {
419
                            "effectiveRule",
489
                    return Object.keys(currentContext).some(key => currentContext[key] !== rule.context[key]) 
420
                            this.circRules,
490
                        && rule[key] !== null
421
                            this.newTriggerNumber,
491
                        && rule[key] !== undefined;
422
                            params
492
                }
423
                        )
493
            );
424
                    } else {
494
425
                        const regex = /overdue_(\d+)_delay/g
495
            // Function to calculate specificity score
426
                        const numberOfTriggers = Object.keys(rules[0]).filter(
496
            const getSpecificityScore = ruleContext => {
427
                            key => regex.test(key) && rules[0][key] !== null
497
                let score = 0
428
                        ).length
498
                if (
429
                        this.newTriggerNumber = editMode
499
                    ruleContext.library_id !== "*" &&
430
                            ? routeParams.triggerNumber
500
                    ruleContext.library_id === currentContext.library_id
431
                            : numberOfTriggers + 1
501
                )
432
                        const { rulesPerTrigger } =
502
                    score += 4
433
                            this.splitCircRulesByTriggerNumber(rules)
503
                if (
434
                        const triggerToDisplay = editMode
504
                    ruleContext.patron_category_id !== "*" &&
435
                            ? routeParams.triggerNumber
505
                    ruleContext.patron_category_id ===
436
                            : numberOfTriggers
506
                    currentContext.patron_category_id
437
                        this.assignTriggerValues(
507
                )
438
                            "circRuleTrigger",
508
                    score += 2
439
                            rulesPerTrigger,
509
                if (
440
                            triggerToDisplay
510
                    ruleContext.item_type_id !== "*" &&
441
                        )
511
                    ruleContext.item_type_id === currentContext.item_type_id
442
                        this.assignTriggerValues(
512
                )
443
                            "effectiveRule",
513
                    score += 1
444
                            rulesPerTrigger,
514
                return score
445
                            triggerToDisplay
515
            }
446
                        )
516
447
                    }
517
            // Sort the rules based on specificity score, descending
448
                },
518
            const sortedRules = relevantRules.sort((a, b) => {
449
                error => {}
519
                return (
450
            )
520
                    getSpecificityScore(b.context) -
521
                    getSpecificityScore(a.context)
522
                )
523
            })
524
525
            // If no rule found, return null
526
            if (sortedRules.length === 0) {
527
                return null 
528
            }
529
530
            // Get the value from the most specific rule
531
            const bestRule = sortedRules[0]
532
            return bestRule[key]
451
        },
533
        },
452
        assignTriggerValues(prop, rules, triggerNumber, context = null) {
534
        assignTriggerValues(rules, triggerNumber, context = null) {
453
            this[prop] = {
535
            this.newRule = {
454
                item_type_id: context
536
                item_type_id: context
455
                    ? context.item_type_id
537
                    ? context.item_type_id
456
                    : rules[triggerNumber - 1].context.item_type_id || "*",
538
                    : rules[triggerNumber - 1].context.item_type_id || "*",
Lines 461-481 export default { Link Here
461
                    ? context.patron_category_id
543
                    ? context.patron_category_id
462
                    : rules[triggerNumber - 1].context.patron_category_id ||
544
                    : rules[triggerNumber - 1].context.patron_category_id ||
463
                      "*",
545
                      "*",
464
                delay: rules[triggerNumber - 1][
546
                delay: rules[triggerNumber - 1] ? rules[triggerNumber - 1][
465
                    `overdue_${triggerNumber}_delay`
547
                    `overdue_${triggerNumber}_delay`
466
                ],
548
                ] : null,
467
                notice: rules[triggerNumber - 1][
549
                notice: rules[triggerNumber - 1] ? rules[triggerNumber - 1][
468
                    `overdue_${triggerNumber}_notice`
550
                    `overdue_${triggerNumber}_notice`
469
                ],
551
                ] : null,
470
                mtt: rules[triggerNumber - 1][`overdue_${triggerNumber}_mtt`]
552
                mtt: rules[triggerNumber - 1] ? rules[triggerNumber - 1][`overdue_${triggerNumber}_mtt`]
471
                    ? rules[triggerNumber - 1][
553
                    ? rules[triggerNumber - 1][
472
                          `overdue_${triggerNumber}_mtt`
554
                          `overdue_${triggerNumber}_mtt`
473
                      ].split(",")
555
                      ].split(",")
474
                    : [],
556
                    : [] : null,
475
                restrict:
557
                restrict: rules[triggerNumber - 1] ?
476
                    rules[triggerNumber - 1][
558
                    rules[triggerNumber - 1][
477
                        `overdue_${triggerNumber}_restrict`
559
                        `overdue_${triggerNumber}_restrict`
478
                    ],
560
                    ] : null,
561
            }
562
            this.fallbackRule = {
563
                delay: this.findFallbackRule(context, `overdue_${triggerNumber}_delay`),
564
                notice: this.findFallbackRule(context, `overdue_${triggerNumber}_notice`),
565
                mtt: this.findFallbackRule(context, `overdue_${triggerNumber}_mtt`),
566
                restrict: this.findFallbackRule(context, `overdue_${triggerNumber}_restrict`)
479
            }
567
            }
480
        },
568
        },
481
    },
569
    },
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/TriggersTable.vue (-8 / +58 lines)
Lines 36-48 Link Here
36
            </thead>
36
            </thead>
37
            <tbody>
37
            <tbody>
38
                <tr
38
                <tr
39
                    v-for="(rule, i) in filterCircRulesByTabNumber(
39
                    v-for="(rule, i) in modal ? filterCircRulesByContext(ruleBeingEdited) : filterCircRulesByTabNumber(
40
                        triggerNumber
40
                        triggerNumber
41
                    )"
41
                    )"
42
                    v-bind:key="'rule' + i"
42
                    v-bind:key="'rule' + i"
43
                    :class="{
43
                    :class="{
44
                        selected_rule:
44
                        selected_rule:
45
                            modal && i + 1 === parseInt(ruleBeingEdited),
45
                            modal && i + 1 === parseInt(triggerBeingEdited),
46
                    }"
46
                    }"
47
                >
47
                >
48
                    <td v-if="!modal">
48
                    <td v-if="!modal">
Lines 100-109 Link Here
100
                            }"
100
                            }"
101
                        >
101
                        >
102
                            {{
102
                            {{
103
                                findEffectiveRule(rule, `overdue_${modal ? i + 1 : triggerNumber}_notice`).value !== '' ? 
103
                                handleTransport(
104
                                handleTransport(
104
                                    findEffectiveRule(rule, `overdue_${modal ? i + 1 : triggerNumber}_mtt`).value,
105
                                    findEffectiveRule(rule, `overdue_${modal ? i + 1 : triggerNumber}_mtt`).value,
105
                                    "email"
106
                                    "email"
106
                                )
107
                                ) : ''
107
                            }}
108
                            }}
108
                        </span>
109
                        </span>
109
                    </td>
110
                    </td>
Lines 116-125 Link Here
116
                            }"
117
                            }"
117
                        >
118
                        >
118
                            {{
119
                            {{
120
                                findEffectiveRule(rule, `overdue_${modal ? i + 1 : triggerNumber}_notice`).value !== '' ? 
119
                                handleTransport(
121
                                handleTransport(
120
                                    findEffectiveRule(rule, `overdue_${modal ? i + 1 : triggerNumber}_mtt`).value,
122
                                    findEffectiveRule(rule, `overdue_${modal ? i + 1 : triggerNumber}_mtt`).value,
121
                                    "print"
123
                                    "print"
122
                                )
124
                                ) : ''
123
                            }}
125
                            }}
124
                        </span>
126
                        </span>
125
                    </td>
127
                    </td>
Lines 132-141 Link Here
132
                            }"
134
                            }"
133
                        >
135
                        >
134
                            {{
136
                            {{
137
                                findEffectiveRule(rule, `overdue_${modal ? i + 1 : triggerNumber}_notice`).value !== '' ? 
135
                                handleTransport(
138
                                handleTransport(
136
                                    findEffectiveRule(rule, `overdue_${modal ? i + 1 : triggerNumber}_mtt`).value,
139
                                    findEffectiveRule(rule, `overdue_${modal ? i + 1 : triggerNumber}_mtt`).value,
137
                                    "sms"
140
                                    "sms"
138
                                )
141
                                ) : ''
139
                            }}
142
                            }}
140
                        </span>
143
                        </span>
141
                    </td>
144
                    </td>
Lines 185-190 export default { Link Here
185
        "triggerNumber",
188
        "triggerNumber",
186
        "modal",
189
        "modal",
187
        "ruleBeingEdited",
190
        "ruleBeingEdited",
191
        "triggerBeingEdited",
188
        "categories",
192
        "categories",
189
        "itemTypes",
193
        "itemTypes",
190
        "letters",
194
        "letters",
Lines 204-211 export default { Link Here
204
        handleRestrictions(value) {
208
        handleRestrictions(value) {
205
            return value === "1" ? this.$__("Yes") : this.$__("No")
209
            return value === "1" ? this.$__("Yes") : this.$__("No")
206
        },
210
        },
211
        filterCircRulesByContext(effectiveRule) {
212
            const context = effectiveRule.context;
213
        
214
            // Filter rules that match the context
215
            let contextRules = this.circRules.filter(rule => {
216
                return Object.keys(context).every(key => {
217
                    return context[key] === rule.context[key];
218
                });
219
            });
220
        
221
            // Calculate the number of 'overdue_X_' triggers in the effectiveRule
222
            const regex = /overdue_(\d+)_delay/g;
223
            const numberOfTriggers = Object.keys(effectiveRule).filter(
224
                key => regex.test(key) && effectiveRule[key] !== null
225
            ).length;
226
        
227
            // Ensure there is one contextRule per 'X' from 1 to numberOfTriggers
228
            for (let i = 1; i <= numberOfTriggers; i++) {
229
                // Check if there's already a rule for overdue_X_ in contextRules
230
                const matchingRule = contextRules.find(rule => rule[`overdue_${i}_delay`] !== undefined);
231
        
232
                if (!matchingRule) {
233
                    // Create a new rule with the same context and null overdue_X_* keys
234
                    const newRule = {
235
                        context: { ...context }, // Clone the context
236
                        [`overdue_${i}_delay`]: null,
237
                        [`overdue_${i}_notice`]: null,
238
                        [`overdue_${i}_mtt`]: null,
239
                        [`overdue_${i}_restrict`]: null
240
                    };
241
        
242
                    // Add the new rule to contextRules
243
                    contextRules.push(newRule);
244
                }
245
            }
246
247
            // Sort contextRules by the 'X' value in 'overdue_X_delay'
248
            contextRules.sort((a, b) => {
249
                const getX = rule => {
250
                    const match = Object.keys(rule).find(key => regex.test(key));
251
                    return match ? parseInt(match.match(/\d+/)[0], 10) : 0;
252
                };
253
        
254
                return getX(a) - getX(b);
255
            });
256
257
            return contextRules;
258
        },
207
        filterCircRulesByTabNumber(number) {
259
        filterCircRulesByTabNumber(number) {
208
            if (this.modal) return this.circRules
209
            return this.circRules.filter(
260
            return this.circRules.filter(
210
                rule =>
261
                rule =>
211
                    rule.triggerNumber === number &&
262
                    rule.triggerNumber === number &&
Lines 226-232 export default { Link Here
226
            if (ruleSet[key] === null) {
277
            if (ruleSet[key] === null) {
227
                // Filter rules to only those with non-null values for the specified key
278
                // Filter rules to only those with non-null values for the specified key
228
                const relevantRules = this.circRules.filter(
279
                const relevantRules = this.circRules.filter(
229
                    rule => rule[key] !== null
280
                    rule => rule[key] !== null && rule[key] !== undefined
230
                )
281
                )
231
282
232
                // Function to calculate specificity score
283
                // Function to calculate specificity score
233
- 

Return to bug 10190