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

(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/letter.tt (-2 lines)
Lines 394-401 Link Here
394
        </div>
394
        </div>
395
        [% END %]
395
        [% END %]
396
396
397
        [% IF code.search('DGST') %] <span class="overdue">Warning, this is a template for a Digest, as such, any references to branch data ( e.g. branches.branchname ) will refer to the borrower's home branch.</span> [% END %]
398
399
        <input type="hidden" id="redirect" name="redirect" value="" />
397
        <input type="hidden" id="redirect" name="redirect" value="" />
400
        <input type="hidden" name="searchfield" value="[% searchfield | html %]" />
398
        <input type="hidden" name="searchfield" value="[% searchfield | html %]" />
401
    </form>
399
    </form>
(-)a/misc/cronjobs/advance_notices.pl (-38 / +108 lines)
Lines 109-114 defaults to date_due,title,author,barcode Link Here
109
Other possible values come from fields in the biblios, items and
109
Other possible values come from fields in the biblios, items and
110
issues tables.
110
issues tables.
111
111
112
=item B<--digest-per-branch>
113
114
Flag to indicate that generation of message digests should be
115
performed separately for each branch.
116
117
A patron could potentially have loans at several different branches
118
There is no natural branch to set as the sender on the aggregated
119
message in this situation so the default behavior is to use the
120
borrowers home branch.  This could surprise to the borrower when
121
message sender is a library where he or she has not borrowed anything.
122
123
Enabling this flag ensures that the issuing library is the sender of
124
the digested message.  It has no effect unless the borrower has
125
choosen 'Digests only' on the advance messages.
126
112
=back
127
=back
113
128
114
=head1 DESCRIPTION
129
=head1 DESCRIPTION
Lines 174-179 my $nomail; # -n: No mai Link Here
174
my $mindays     = 0;                                                # -m: Maximum number of days in advance to send notices
189
my $mindays     = 0;                                                # -m: Maximum number of days in advance to send notices
175
my $maxdays     = 30;                                               # -e: the End of the time period
190
my $maxdays     = 30;                                               # -e: the End of the time period
176
my $verbose     = 0;                                                # -v: verbose
191
my $verbose     = 0;                                                # -v: verbose
192
my $digest_per_branch = 0;                                          # -digest-per-branch: Prepare and send digests per branch
177
my $itemscontent = join(',',qw( date_due title author barcode ));
193
my $itemscontent = join(',',qw( date_due title author barcode ));
178
194
179
my $help    = 0;
195
my $help    = 0;
Lines 186-191 GetOptions( Link Here
186
            'n'              => \$nomail,
202
            'n'              => \$nomail,
187
            'm:i'            => \$maxdays,
203
            'm:i'            => \$maxdays,
188
            'v'              => \$verbose,
204
            'v'              => \$verbose,
205
            'digest-per-branch' => \$digest_per_branch,
189
            'itemscontent=s' => \$itemscontent,
206
            'itemscontent=s' => \$itemscontent,
190
       )or pod2usage(2);
207
       )or pod2usage(2);
191
pod2usage(1) if $help;
208
pod2usage(1) if $help;
Lines 218-225 warn 'found ' . scalar( @$upcoming_dues ) . ' issues' if $verbose; Link Here
218
235
219
# hash of borrowernumber to number of items upcoming
236
# hash of borrowernumber to number of items upcoming
220
# for patrons wishing digests only.
237
# for patrons wishing digests only.
221
my $upcoming_digest;
238
my $upcoming_digest = {};
222
my $due_digest;
239
my $due_digest = {};
223
240
224
my $dbh = C4::Context->dbh();
241
my $dbh = C4::Context->dbh();
225
my $sth = $dbh->prepare(<<'END_SQL');
242
my $sth = $dbh->prepare(<<'END_SQL');
Lines 250-257 UPCOMINGITEM: foreach my $upcoming ( @$upcoming_dues ) { Link Here
250
        
267
        
251
        if ( $borrower_preferences->{'wants_digest'} ) {
268
        if ( $borrower_preferences->{'wants_digest'} ) {
252
            # cache this one to process after we've run through all of the items.
269
            # cache this one to process after we've run through all of the items.
253
            $due_digest->{ $upcoming->{borrowernumber} }->{email} = $from_address;
270
            if ($digest_per_branch) {
254
            $due_digest->{ $upcoming->{borrowernumber} }->{count}++;
271
                $due_digest->{ $upcoming->{branchcode} }->{ $upcoming->{borrowernumber} }->{email} = $from_address;
272
                $due_digest->{ $upcoming->{branchcode} }->{ $upcoming->{borrowernumber} }->{count}++;
273
            } else {
274
                $due_digest->{ $upcoming->{borrowernumber} }->{email} = $from_address;
275
                $due_digest->{ $upcoming->{borrowernumber} }->{count}++;
276
            }
255
        } else {
277
        } else {
256
            my $item = Koha::Items->find( $upcoming->{itemnumber} );
278
            my $item = Koha::Items->find( $upcoming->{itemnumber} );
257
            my $letter_type = 'DUE';
279
            my $letter_type = 'DUE';
Lines 283-290 UPCOMINGITEM: foreach my $upcoming ( @$upcoming_dues ) { Link Here
283
305
284
        if ( $borrower_preferences->{'wants_digest'} ) {
306
        if ( $borrower_preferences->{'wants_digest'} ) {
285
            # cache this one to process after we've run through all of the items.
307
            # cache this one to process after we've run through all of the items.
286
            $upcoming_digest->{ $upcoming->{borrowernumber} }->{email} = $from_address;
308
            if ($digest_per_branch) {
287
            $upcoming_digest->{ $upcoming->{borrowernumber} }->{count}++;
309
                $upcoming_digest->{ $upcoming->{branchcode} }->{ $upcoming->{borrowernumber} }->{email} = $from_address;
310
                $upcoming_digest->{ $upcoming->{branchcode} }->{ $upcoming->{borrowernumber} }->{count}++;
311
            } else {
312
                $upcoming_digest->{ $upcoming->{borrowernumber} }->{email} = $from_address;
313
                $upcoming_digest->{ $upcoming->{borrowernumber} }->{count}++;
314
            }
288
        } else {
315
        } else {
289
            my $item = Koha::Items->find( $upcoming->{itemnumber} );
316
            my $item = Koha::Items->find( $upcoming->{itemnumber} );
290
            my $letter_type = 'PREDUE';
317
            my $letter_type = 'PREDUE';
Lines 342-373 SELECT biblio.*, items.*, issues.* Link Here
342
    AND (TO_DAYS(date_due)-TO_DAYS(NOW()) = ?)
369
    AND (TO_DAYS(date_due)-TO_DAYS(NOW()) = ?)
343
END_SQL
370
END_SQL
344
371
345
send_digests({
372
if ($digest_per_branch) {
346
    sth => $sth_digest,
373
    while (my ($branchcode, $digests) = each %$upcoming_digest) {
347
    digests => $upcoming_digest,
374
        send_digests({
348
    letter_code => 'PREDUEDGST',
375
            sth => $sth_digest,
349
    get_item_info => sub {
376
            digests => $digests,
350
        my $params = shift;
377
            letter_code => 'PREDUEDGST',
351
        $params->{sth}->execute($params->{borrowernumber},
378
            branchcode => $branchcode,
352
                      $params->{borrower_preferences}->{'days_in_advance'});
379
            get_item_info => sub {
353
        return sub {
380
                my $params = shift;
354
            $params->{sth}->fetchrow_hashref;
381
                $params->{sth}->execute($params->{borrowernumber},
355
        };
382
                                        $params->{borrower_preferences}->{'days_in_advance'});
383
                return sub {
384
                    $params->{sth}->fetchrow_hashref;
385
                };
386
            }
387
        });
356
    }
388
    }
357
});
389
358
390
    while (my ($branchcode, $digests) = each %$due_digest) {
359
send_digests({
391
        send_digests({
360
    sth => $sth_digest,
392
            sth => $sth_digest,
361
    digest => $due_digest,
393
            digest => $due_digest,
362
    letter_code => 'DUEGST',
394
            letter_code => 'DUEGST',
363
    get_item_info => sub {
395
            branchcode => $branchcode,
364
        my $params = shift;
396
            get_item_info => sub {
365
        $params->{sth}->execute($params->{borrowernumber}, 0);
397
                my $params = shift;
366
        return sub {
398
                $params->{sth}->execute($params->{borrowernumber}, 0);
367
            $params->{sth}->fetchrow_hashref;
399
                return sub {
368
        };
400
                    $params->{sth}->fetchrow_hashref;
401
                };
402
            }
403
        });
369
    }
404
    }
370
});
405
} else {
406
    send_digests({
407
        sth => $sth_digest,
408
        digests => $upcoming_digest,
409
        letter_code => 'PREDUEDGST',
410
        get_item_info => sub {
411
            my $params = shift;
412
            $params->{sth}->execute($params->{borrowernumber},
413
                                    $params->{borrower_preferences}->{'days_in_advance'});
414
            return sub {
415
                $params->{sth}->fetchrow_hashref;
416
            };
417
        }
418
    });
419
420
    send_digests({
421
        sth => $sth_digest,
422
        digest => $due_digest,
423
        letter_code => 'DUEGST',
424
        get_item_info => sub {
425
            my $params = shift;
426
            $params->{sth}->execute($params->{borrowernumber}, 0);
427
            return sub {
428
                $params->{sth}->fetchrow_hashref;
429
            };
430
        }
431
    });
432
}
371
433
372
=head1 METHODS
434
=head1 METHODS
373
435
Lines 471-476 sub send_digests { Link Here
471
        my $count = $digest->{count};
533
        my $count = $digest->{count};
472
        my $from_address = $digest->{email};
534
        my $from_address = $digest->{email};
473
535
536
        my %branch_info;
537
        my $branchcode;
538
539
        if (defined($params->{branchcode})) {
540
            %branch_info = ();
541
            $branchcode = $params->{branchcode};
542
        } else {
543
            ## Get branch info for borrowers home library.
544
            %branch_info = get_branch_info( $borrowernumber );
545
            $branchcode = $branch_info{'branches.branchcode'};
546
        }
547
474
        my $borrower_preferences =
548
        my $borrower_preferences =
475
            C4::Members::Messaging::GetMessagingPreferences(
549
            C4::Members::Messaging::GetMessagingPreferences(
476
                {
550
                {
Lines 491-499 sub send_digests { Link Here
491
            $titles .= C4::Letters::get_item_content( { item => $item_info, item_content_fields => \@item_content_fields } );
565
            $titles .= C4::Letters::get_item_content( { item => $item_info, item_content_fields => \@item_content_fields } );
492
        }
566
        }
493
567
494
        ## Get branch info for borrowers home library.
495
        my %branch_info = get_branch_info( $borrowernumber );
496
497
        foreach my $transport ( keys %{ $borrower_preferences->{'transports'} } ) {
568
        foreach my $transport ( keys %{ $borrower_preferences->{'transports'} } ) {
498
            my $letter = parse_letter(
569
            my $letter = parse_letter(
499
                {
570
                {
Lines 502-514 sub send_digests { Link Here
502
                    substitute     => {
573
                    substitute     => {
503
                        count           => $count,
574
                        count           => $count,
504
                        'items.content' => $titles,
575
                        'items.content' => $titles,
505
                        %branch_info,
576
                        %branch_info
506
                    },
577
                    },
507
                    branchcode             => $branch_info{"branches.branchcode"},
578
                    branchcode     => $branchcode,
508
                    message_transport_type => $transport,
579
                    message_transport_type => $transport
509
                }
580
                }
510
                )
581
            ) || warn "no letter of type '$params->{letter_type}' found for borrowernumber $borrowernumber. Please see sample_notices.sql";
511
                or warn "no letter of type '$params->{letter_type}' found for borrowernumber $borrowernumber. Please see sample_notices.sql";
512
            push @letters, $letter if $letter;
582
            push @letters, $letter if $letter;
513
        }
583
        }
514
584
(-)a/t/db_dependent/cronjobs/advance_notices_digest.t (-117 / +194 lines)
Lines 19-25 Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Test::More tests => 3;
22
use Test::More tests => 8;
23
use t::lib::TestBuilder;
23
use t::lib::TestBuilder;
24
use DateTime;
24
use DateTime;
25
use File::Spec;
25
use File::Spec;
Lines 30-184 my $scriptDir = dirname(File::Spec->rel2abs( __FILE__ )); Link Here
30
30
31
my $dbh = C4::Context->dbh;
31
my $dbh = C4::Context->dbh;
32
32
33
# Set only to avoid exception.
33
my $library1;
34
$ENV{"OVERRIDE_SYSPREF_dateformat"} = 'metric';
34
my $library2;
35
35
my $library3;
36
$dbh->{AutoCommit} = 0;
36
my $borrower;
37
$dbh->{RaiseError} = 1;
37
38
38
sub build_test_objects {
39
my $builder = t::lib::TestBuilder->new;
39
    $dbh->{AutoCommit} = 0;
40
40
    $dbh->{RaiseError} = 1;
41
my $library1 = $builder->build({
41
42
    source => 'Branch',
42
    # Set only to avoid exception.
43
});
43
    $ENV{"OVERRIDE_SYSPREF_dateformat"} = 'metric';
44
my $library2 = $builder->build({
44
45
    source => 'Branch',
45
    my $builder = t::lib::TestBuilder->new;
46
});
46
47
my $library3 = $builder->build({
47
    $library1 = $builder->build({
48
    source => 'Branch',
48
        source => 'Branch',
49
});
49
    });
50
my $borrower = $builder->build({
50
    $library2 = $builder->build({
51
    source => 'Borrower',
51
        source => 'Branch',
52
    value => {
52
    });
53
        branchcode => $library1->{branchcode},
53
    $library3 = $builder->build({
54
    }
54
        source => 'Branch',
55
});
55
    });
56
$dbh->do(<<DELETESQL);
56
    $borrower = $builder->build({
57
        source => 'Borrower',
58
        value => {
59
            branchcode => $library1->{branchcode},
60
        }
61
    });
62
    $dbh->do(<<DELETESQL);
57
DELETE FROM letter
63
DELETE FROM letter
58
 WHERE module='circulation'
64
 WHERE module='circulation'
59
   AND code = 'PREDUEDGST'
65
   AND code = 'PREDUEDGST'
60
   AND message_transport_type='email'
66
   AND message_transport_type='email'
61
   AND branchcode=''
67
   AND branchcode=''
62
DELETESQL
68
DELETESQL
63
$dbh->do(<<DELETESQL);
69
70
    $dbh->do(<<DELETESQL);
64
DELETE FROM message_attributes WHERE message_name = 'Advance_Notice'
71
DELETE FROM message_attributes WHERE message_name = 'Advance_Notice'
65
DELETESQL
72
DELETESQL
66
73
67
my $message_attribute = $builder->build({
74
    my $message_attribute = $builder->build({
68
    source => 'MessageAttribute',
75
        source => 'MessageAttribute',
69
    value => {
76
        value => {
70
        message_name => 'Advance_Notice'
77
            message_name => 'Advance_Notice'
71
    }
78
        }
72
});
79
    });
73
74
my $letter = $builder->build({
75
    source => 'Letter',
76
    value => {
77
        module => 'circulation',
78
        code => 'PREDUEDGST',
79
        branchcode => '',
80
        message_transport_type => 'email',
81
        lang => 'default',
82
        is_html => 0,
83
        content => '<<count>> <<branches.branchname>>'
84
    }
85
});
86
my $borrower_message_preference = $builder->build({
87
    source => 'BorrowerMessagePreference',
88
    value => {
89
        borrowernumber => $borrower->{borrowernumber},
90
        wants_digest => 1,
91
        days_in_advance => 1,
92
        message_attribute_id => $message_attribute->{message_attribute_id}
93
    }
94
});
95
80
96
my $borrower_message_transport_preference = $builder->build({
81
    my $letter = $builder->build({
97
    source => 'BorrowerMessageTransportPreference',
82
        source => 'Letter',
98
    value => {
83
        value => {
99
        borrower_message_preference_id => $borrower_message_preference->{borrower_message_preference_id},
84
            module => 'circulation',
100
        message_transport_type => 'email'
85
            code => 'PREDUEDGST',
101
    }
86
            branchcode => '',
102
});
87
            message_transport_type => 'email',
103
88
            lang => 'default',
104
my $biblio = $builder->build({
89
            is_html => 0,
105
    source => 'Biblio',
90
            content => '<<count>> <<branches.branchname>>'
106
});
91
        }
107
my $biblioitem = $builder->build({
92
    });
108
    source => 'Biblioitem',
93
    my $borrower_message_preference = $builder->build({
109
    value => {
94
        source => 'BorrowerMessagePreference',
110
        biblionumber => $biblio->{biblionumber}
95
        value => {
111
    }
96
            borrowernumber => $borrower->{borrowernumber},
112
});
97
            wants_digest => 1,
113
my $item1 = $builder->build({
98
            days_in_advance => 1,
114
    source => 'Item'
99
            message_attribute_id => $message_attribute->{message_attribute_id}
115
});
100
        }
116
my $item2 = $builder->build({
101
    });
117
    source => 'Item'
102
118
});
103
    my $borrower_message_transport_preference = $builder->build({
119
my $now = DateTime->now();
104
        source => 'BorrowerMessageTransportPreference',
120
my $tomorrow = $now->add(days => 1)->strftime('%F');
105
        value => {
121
106
            borrower_message_preference_id => $borrower_message_preference->{borrower_message_preference_id},
122
my $issue1 = $builder->build({
107
            message_transport_type => 'email'
123
    source => 'Issue',
108
        }
124
    value => {
109
    });
125
        date_due => $tomorrow,
126
        itemnumber => $item1->{itemnumber},
127
        branchcode => $library1->{branchcode},
128
        borrowernumber => $borrower->{borrowernumber},
129
        returndate => undef
130
    }
131
});
132
133
my $issue2 = $builder->build({
134
    source => 'Issue',
135
    value => {
136
        date_due => $tomorrow,
137
        itemnumber => $item2->{itemnumber},
138
        branchcode => $library2->{branchcode},
139
        branchcode => $library3->{branchcode},
140
        borrowernumber => $borrower->{borrowernumber},
141
        returndate => undef
142
    }
143
});
144
110
145
C4::Context->set_preference('EnhancedMessagingPreferences', 1);
111
    my $biblio = $builder->build({
112
        source => 'Biblio',
113
    });
114
    my $biblioitem = $builder->build({
115
        source => 'Biblioitem',
116
        value => {
117
            biblionumber => $biblio->{biblionumber}
118
        }
119
    });
120
    my $item1 = $builder->build({
121
        source => 'Item'
122
    });
123
    my $item2 = $builder->build({
124
        source => 'Item'
125
    });
126
    my $item3 = $builder->build({
127
        source => 'Item'
128
    });
129
    my $now = DateTime->now();
130
    my $tomorrow = $now->add(days => 1)->strftime('%F');
146
131
147
my $script = '';
132
    my $issue1 = $builder->build({
133
        source => 'Issue',
134
        value => {
135
            date_due => $tomorrow,
136
            itemnumber => $item1->{itemnumber},
137
            branchcode => $library2->{branchcode},
138
            borrowernumber => $borrower->{borrowernumber},
139
            returndate => undef
140
        }
141
    });
142
143
    my $issue2 = $builder->build({
144
        source => 'Issue',
145
        value => {
146
            date_due => $tomorrow,
147
            itemnumber => $item2->{itemnumber},
148
            branchcode => $library3->{branchcode},
149
            borrowernumber => $borrower->{borrowernumber},
150
            returndate => undef
151
        }
152
                                 });
153
    my $issue3 = $builder->build({
154
        source => 'Issue',
155
        value => {
156
            date_due => $tomorrow,
157
            itemnumber => $item3->{itemnumber},
158
            branchcode => $library3->{branchcode},
159
            borrowernumber => $borrower->{borrowernumber},
160
            returndate => undef
161
        }
162
    });
163
164
    C4::Context->set_preference('EnhancedMessagingPreferences', 1);
165
}
166
167
sub run_script {
168
    my $script = shift;
169
    local @ARGV = @_;
170
171
    ## no critic
172
173
    # We simulate script execution by evaluating the script code in the context
174
    # of this unit test.
175
176
    eval $script; #Violates 'ProhibitStringyEval'
177
178
    ## use critic
179
180
    die $@ if $@;
181
}
182
183
my $scriptContent = '';
148
my $scriptFile = "$scriptDir/../../../misc/cronjobs/advance_notices.pl";
184
my $scriptFile = "$scriptDir/../../../misc/cronjobs/advance_notices.pl";
149
open my $scriptfh, "<", $scriptFile or die "Failed to open $scriptFile: $!";
185
open my $scriptfh, "<", $scriptFile or die "Failed to open $scriptFile: $!";
150
186
151
while (<$scriptfh>) {
187
while (<$scriptfh>) {
152
    $script .= $_;
188
    $scriptContent .= $_;
153
}
189
}
154
close $scriptfh;
190
close $scriptfh;
155
191
156
@ARGV = ('advanced_notices.pl', '-c');
192
my $sthmq = $dbh->prepare('SELECT * FROM message_queue WHERE borrowernumber = ?');
157
158
## no critic
159
160
# We simulate script execution by evaluating the script code in the context
161
# of this unit test.
162
193
163
eval $script; #Violates 'ProhibitStringyEval'
194
#
195
# Test default behavior
196
#
164
197
165
## use critic
198
build_test_objects();
166
199
167
die $@ if $@;
200
run_script($scriptContent, 'advanced_notices.pl', '-c');
168
201
169
my $sthmq = $dbh->prepare('SELECT * FROM message_queue WHERE borrowernumber = ?');
170
$sthmq->execute($borrower->{borrowernumber});
202
$sthmq->execute($borrower->{borrowernumber});
171
203
172
my $messages = $sthmq->fetchall_hashref('message_id');
204
my $messages = $sthmq->fetchall_hashref('message_id');
173
205
174
is(scalar(keys %$messages), 1, 'There is one message in the queue');
206
is(scalar(keys %$messages), 1, 'There is one message in the queue');
207
175
for my $message (keys %$messages) {
208
for my $message (keys %$messages) {
176
    $messages->{$message}->{content} =~ /(\d+) (.*)/;
209
    $messages->{$message}->{content} =~ /(\d+) (.*)/;
177
    my $count = $1;
210
    my $count = $1;
178
    my $branchname = $2;
211
    my $branchname = $2;
179
212
180
    is ($count, '2', 'Issue count is 2');
213
    is ($count, '3', 'Issue count is 3');
181
    is ($branchname, $library1->{branchname}, 'Branchname is that of borrowers home branch.');
214
    is ($branchname, $library1->{branchname}, 'Branchname is that of borrowers home branch.');
182
}
215
}
183
216
184
$dbh->rollback;
217
$dbh->rollback;
185
- 
218
219
#
220
# Test -digest-per-branch
221
#
222
223
build_test_objects();
224
225
run_script($scriptContent, 'advanced_notices.pl', '-c', '-digest-per-branch');
226
227
$sthmq->execute($borrower->{borrowernumber});
228
229
$messages = $sthmq->fetchall_hashref('message_id');
230
231
is(scalar(keys %$messages), 2, 'There are two messages in the queue');
232
233
my %expected = (
234
    $library2->{branchname} => {
235
        count => 1,
236
    },
237
    $library3->{branchname} => {
238
        count => 2,
239
    }
240
 );
241
242
my %expected_branchnames = (
243
    $library2->{branchname} => 1,
244
    $library3->{branchname} => 1
245
);
246
247
my $i = 0;
248
for my $message (keys %$messages) {
249
    $messages->{$message}->{content} =~ /(\d+) (.*)/;
250
    my $count = $1;
251
    my $branchname = $2;
252
253
    ok ($expected_branchnames{$branchname}, 'Branchname is that of expected issuing branch.');
254
255
    $expected_branchnames{$branchname} = 0;
256
257
    is ($count, $expected{$branchname}->{count}, 'Issue count is ' . $expected{$branchname}->{count});
258
259
    $i++;
260
}
261
262
$dbh->rollback;

Return to bug 20478