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

Return to bug 10190