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

(-)a/Koha/Patron/Category.pm (-1 / +2 lines)
Lines 325-331 sub to_api_mapping { Link Here
325
        exclude_from_local_holds_priority      => 'exclude_from_local_holds_priority',
325
        exclude_from_local_holds_priority      => 'exclude_from_local_holds_priority',
326
        noissuescharge                         => 'no_issues_charge',
326
        noissuescharge                         => 'no_issues_charge',
327
        noissueschargeguarantees               => 'no_issues_charge_guarantees',
327
        noissueschargeguarantees               => 'no_issues_charge_guarantees',
328
        noissueschargeguarantorswithguarantees => 'no_issues_charge_guarantors_with_guarantees'
328
        noissueschargeguarantorswithguarantees => 'no_issues_charge_guarantors_with_guarantees',
329
        print_notice_charge                    => 'print_notice_charge'
329
    };
330
    };
330
}
331
}
331
332
(-)a/misc/cronjobs/gather_print_notices.pl (-34 / +23 lines)
Lines 202-211 sub print_notices { Link Here
202
            );
202
            );
203
        }
203
        }
204
204
205
        if ( $send ) {
205
        if ($send) {
206
            foreach my $message ( @$branch_messages ) {
206
            foreach my $message (@$branch_messages) {
207
207
                # Apply print notice charges if enabled
208
                # Apply print notice charges if enabled
208
                if (!$skip_charges) {
209
                if ( !$skip_charges ) {
209
                    apply_print_notice_charge($message);
210
                    apply_print_notice_charge($message);
210
                }
211
                }
211
212
Lines 363-412 sub apply_print_notice_charge { Link Here
363
    my ($message) = @_;
364
    my ($message) = @_;
364
365
365
    # Check for valid message data
366
    # Check for valid message data
366
    unless ($message && ref($message) eq 'HASH') {
367
    unless ( $message && ref($message) eq 'HASH' && $message->{borrowernumber} ) {
367
        warn "Invalid message data passed to apply_print_notice_charge";
368
        warn "Invalid message data passed to apply_print_notice_charge";
368
        return;
369
        return;
369
    }
370
    }
370
371
371
    # Validate borrowernumber is numeric to prevent injection
372
    my $patron = Koha::Patrons->find( $message->{borrowernumber} );
372
    unless ($message->{borrowernumber} && $message->{borrowernumber} =~ /^\d+$/) {
373
        warn "Invalid or missing borrowernumber in message for print notice charge";
374
        return;
375
    }
376
377
    # Validate branchcode format if present
378
    if (defined $message->{branchcode} && $message->{branchcode} !~ /^[A-Za-z0-9_-]*$/) {
379
        warn "Invalid branchcode format in message: " . $message->{branchcode};
380
        return;
381
    }
382
383
    # Validate letter_code format if present
384
    if (defined $message->{letter_code} && $message->{letter_code} !~ /^[A-Za-z0-9_-]*$/) {
385
        warn "Invalid letter_code format in message: " . $message->{letter_code};
386
        return;
387
    }
388
389
    # Skip if patron not found (using validated borrowernumber)
390
    my $patron = Koha::Patrons->find($message->{borrowernumber});
391
    unless ($patron) {
373
    unless ($patron) {
392
        warn "Patron " . $message->{borrowernumber} . " not found for print notice charge";
374
        warn "Patron " . $message->{borrowernumber} . " not found for print notice charge";
393
        return;
375
        return;
394
    }
376
    }
395
377
396
    eval {
378
    eval {
397
        my $result = $patron->add_print_notice_charge_if_needed({
379
        my $result = $patron->add_print_notice_charge_if_needed(
398
            notice_code => $message->{letter_code},
380
            {
399
            library_id  => $message->{branchcode},
381
                notice_code => $message->{letter_code},
400
        });
382
                library_id  => $message->{branchcode},
383
            }
384
        );
401
385
402
        # Log successful charge application
386
        # Log successful charge application
403
        if ($result) {
387
        if ($result) {
404
        cronlogaction({
388
            cronlogaction(
405
            action => 'Print notice charge',
389
                {
406
            info   => "Applied charge for patron " . $message->{borrowernumber} .
390
                    action => 'Print notice charge',
407
                         " notice " . ($message->{letter_code} || 'unknown') .
391
                    info   => "Applied charge for patron "
408
                         " (charge ID: " . $result->id . ")"
392
                        . $message->{borrowernumber}
409
        });
393
                        . " notice "
394
                        . ( $message->{letter_code} || 'unknown' )
395
                        . " (charge ID: "
396
                        . $result->id . ")"
397
                }
398
            );
410
        }
399
        }
411
    };
400
    };
412
401
(-)a/t/db_dependent/Koha/Account/PrintNoticeCharges.t (-274 lines)
Lines 1-274 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2024 Koha Development team
4
#
5
# This file is part of Koha
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>
19
20
use Modern::Perl;
21
22
use Test::More tests => 8;
23
use Test::MockModule;
24
use Test::Exception;
25
use Test::Warn;
26
27
use C4::Context;
28
use Koha::Account;
29
use Koha::Account::Lines;
30
use Koha::Patrons;
31
32
use t::lib::Mocks;
33
use t::lib::TestBuilder;
34
35
my $schema = Koha::Database->new->schema;
36
$schema->storage->dbh->{PrintError} = 0;
37
my $builder = t::lib::TestBuilder->new;
38
C4::Context->interface('commandline');
39
40
subtest 'add_print_notice_charge_if_needed with charging disabled' => sub {
41
    plan tests => 3;
42
43
    $schema->storage->txn_begin;
44
45
    # Create category with print notice charging disabled (0.00)
46
    my $category = $builder->build_object({ class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.00 } });
47
    my $patron = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $category->categorycode } });
48
    my $account = Koha::Account->new({ patron_id => $patron->borrowernumber });
49
50
    my $initial_balance = $account->balance;
51
52
    my $result = $patron->add_print_notice_charge_if_needed({
53
        notice_code => 'ODUE',
54
        library_id => $patron->branchcode
55
    });
56
57
    is($result, undef, 'No charge applied when charging disabled');
58
    is($account->balance, $initial_balance, 'Account balance unchanged');
59
    is($account->lines->count, 0, 'No account lines created');
60
61
    $schema->storage->txn_rollback;
62
};
63
64
subtest 'add_print_notice_charge_if_needed with charging enabled' => sub {
65
    plan tests => 7;
66
67
    $schema->storage->txn_begin;
68
69
    # Create category with print notice charging enabled (0.75)
70
    my $category = $builder->build_object({ class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.75 } });
71
    my $patron = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $category->categorycode } });
72
    my $account = Koha::Account->new({ patron_id => $patron->borrowernumber });
73
74
    my $initial_balance = $account->balance;
75
76
    my $result = $patron->add_print_notice_charge_if_needed({
77
        notice_code => 'ODUE',
78
        library_id => $patron->branchcode
79
    });
80
81
    isa_ok($result, 'Koha::Account::Line', 'Returns account line object');
82
    is($account->balance, $initial_balance + 0.75, 'Account balance increased by charge amount');
83
    is($result->debit_type_code, 'PRINT_NOTICE', 'Correct debit type applied');
84
    cmp_ok($result->amount, '==', 0.75, 'Correct amount charged');
85
    cmp_ok($result->amountoutstanding, '==', 0.75, 'Full amount outstanding');
86
    is($result->borrowernumber, $patron->borrowernumber, 'Correct patron charged');
87
    is($result->interface, 'commandline', 'Correct interface recorded');
88
89
    $schema->storage->txn_rollback;
90
};
91
92
subtest 'add_print_notice_charge_if_needed with custom amount' => sub {
93
    plan tests => 3;
94
95
    $schema->storage->txn_begin;
96
97
    # Create category with print notice charging enabled (0.50)
98
    my $category = $builder->build_object({ class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.50 } });
99
    my $patron = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $category->categorycode } });
100
    my $account = Koha::Account->new({ patron_id => $patron->borrowernumber });
101
102
    my $custom_amount = 1.25;
103
    my $result = $patron->add_print_notice_charge_if_needed({
104
        notice_code => 'PREDUE',
105
        library_id => $patron->branchcode,
106
        amount => $custom_amount
107
    });
108
109
    isa_ok($result, 'Koha::Account::Line', 'Returns account line object');
110
    is($result->amount, $custom_amount, 'Custom amount used instead of system preference');
111
    is($account->balance, $custom_amount, 'Account balance reflects custom amount');
112
113
    $schema->storage->txn_rollback;
114
};
115
116
subtest 'add_print_notice_charge_if_needed with zero amount' => sub {
117
    plan tests => 3;
118
119
    $schema->storage->txn_begin;
120
121
    # Create category with charging disabled (0.00)
122
    my $category = $builder->build_object({ class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.00 } });
123
    my $patron = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $category->categorycode } });
124
    my $account = Koha::Account->new({ patron_id => $patron->borrowernumber });
125
126
    my $result = $patron->add_print_notice_charge_if_needed({
127
        notice_code => 'HOLD',
128
        library_id => $patron->branchcode
129
    });
130
131
    is($result, undef, 'No charge applied when amount is zero');
132
    is($account->balance, 0, 'Account balance unchanged');
133
    is($account->lines->count, 0, 'No account lines created');
134
135
    $schema->storage->txn_rollback;
136
};
137
138
subtest 'add_print_notice_charge_if_needed validation tests' => sub {
139
    plan tests => 3;
140
141
    $schema->storage->txn_begin;
142
143
    # Create category with print notice charging enabled
144
    my $category = $builder->build_object({ class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.50 } });
145
    my $patron = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $category->categorycode } });
146
147
    # Test with invalid negative amount
148
    my $result1 = $patron->add_print_notice_charge_if_needed({
149
        notice_code => 'ODUE',
150
        library_id => $patron->branchcode,
151
        amount => -0.50
152
    });
153
    is($result1, undef, 'No charge applied for negative amount');
154
155
    # Test with invalid non-numeric amount
156
    my $result2 = $patron->add_print_notice_charge_if_needed({
157
        notice_code => 'ODUE',
158
        library_id => $patron->branchcode,
159
        amount => 'invalid'
160
    });
161
    is($result2, undef, 'No charge applied for non-numeric amount');
162
163
    # Test with valid amount works
164
    my $result3 = $patron->add_print_notice_charge_if_needed({
165
        notice_code => 'ODUE',
166
        library_id => $patron->branchcode,
167
        amount => 1.00
168
    });
169
    isa_ok($result3, 'Koha::Account::Line', 'Valid amount works correctly');
170
171
    $schema->storage->txn_rollback;
172
};
173
174
subtest 'add_print_notice_charge_if_needed without notice code' => sub {
175
    plan tests => 2;
176
177
    $schema->storage->txn_begin;
178
179
    # Create category with print notice charging enabled (0.50)
180
    my $category = $builder->build_object({ class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.50 } });
181
    my $patron = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $category->categorycode } });
182
    my $account = Koha::Account->new({ patron_id => $patron->borrowernumber });
183
184
    my $result = $patron->add_print_notice_charge_if_needed({
185
        library_id => $patron->branchcode
186
    });
187
188
    isa_ok($result, 'Koha::Account::Line', 'Returns account line object even without notice code');
189
    cmp_ok($result->amount, '==', 0.50, 'Category amount used');
190
191
    $schema->storage->txn_rollback;
192
};
193
194
subtest 'add_print_notice_charge_if_needed library_id handling' => sub {
195
    plan tests => 4;
196
197
    $schema->storage->txn_begin;
198
199
    # Create category with print notice charging enabled (0.50)
200
    my $category = $builder->build_object({ class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.50 } });
201
    my $patron = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $category->categorycode } });
202
    my $account = Koha::Account->new({ patron_id => $patron->borrowernumber });
203
204
    # Get the patron's library for testing
205
    my $patron_library = $patron->branchcode;
206
207
    # Mock userenv for default library
208
    my $mock_context = Test::MockModule->new('C4::Context');
209
    $mock_context->mock('userenv', sub {
210
        return { branch => $patron_library };
211
    });
212
213
    # Test with explicit library_id
214
    my $result1 = $patron->add_print_notice_charge_if_needed({
215
        notice_code => 'ODUE',
216
        library_id => $patron_library
217
    });
218
    is($result1->branchcode, $patron_library, 'Explicit library_id used when provided');
219
220
    # Test without library_id (should use userenv)
221
    my $result2 = $patron->add_print_notice_charge_if_needed({
222
        notice_code => 'PREDUE'
223
    });
224
    is($result2->branchcode, $patron_library, 'Default library from userenv used when not provided');
225
226
    # Test with undefined userenv
227
    $mock_context->mock('userenv', sub { return; });
228
    my $result3 = $patron->add_print_notice_charge_if_needed({
229
        notice_code => 'HOLD'
230
    });
231
    isa_ok($result3, 'Koha::Account::Line', 'Still works with undefined userenv');
232
    is($result3->branchcode, undef, 'Library_id is undef when no userenv');
233
234
    $schema->storage->txn_rollback;
235
};
236
237
subtest 'add_print_notice_charge_if_needed category isolation test' => sub {
238
    plan tests => 6;
239
240
    $schema->storage->txn_begin;
241
242
    # Create two categories - one with charging enabled, one disabled
243
    my $category_enabled = $builder->build_object({ class => 'Koha::Patron::Categories', value => { print_notice_charge => 1.25 } });
244
    my $category_disabled = $builder->build_object({ class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.00 } });
245
246
    # Create patrons in each category
247
    my $patron_enabled = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $category_enabled->categorycode } });
248
    my $patron_disabled = $builder->build_object({ class => 'Koha::Patrons', value => { categorycode => $category_disabled->categorycode } });
249
250
    my $account_enabled = Koha::Account->new({ patron_id => $patron_enabled->borrowernumber });
251
    my $account_disabled = Koha::Account->new({ patron_id => $patron_disabled->borrowernumber });
252
253
    # Test patron with enabled category gets charged
254
    my $result_enabled = $patron_enabled->add_print_notice_charge_if_needed({
255
        notice_code => 'ODUE',
256
        library_id => $patron_enabled->branchcode
257
    });
258
259
    isa_ok($result_enabled, 'Koha::Account::Line', 'Patron with enabled category gets charged');
260
    cmp_ok($result_enabled->amount, '==', 1.25, 'Correct amount from enabled category');
261
    cmp_ok($account_enabled->balance, '==', 1.25, 'Enabled patron account balance updated');
262
263
    # Test patron with disabled category does not get charged
264
    my $result_disabled = $patron_disabled->add_print_notice_charge_if_needed({
265
        notice_code => 'ODUE',
266
        library_id => $patron_disabled->branchcode
267
    });
268
269
    is($result_disabled, undef, 'Patron with disabled category not charged');
270
    cmp_ok($account_disabled->balance, '==', 0, 'Disabled patron account balance unchanged');
271
    is($account_disabled->lines->count, 0, 'No account lines for disabled category patron');
272
273
    $schema->storage->txn_rollback;
274
};
(-)a/t/db_dependent/Koha/Patron/PrintNoticeCharge.t (+309 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2024 Koha Development team
4
#
5
# This file is part of Koha
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>
19
20
use Modern::Perl;
21
22
use Test::More tests => 8;
23
use Test::MockModule;
24
use Test::Exception;
25
use Test::Warn;
26
27
use C4::Context;
28
use Koha::Account;
29
use Koha::Account::Lines;
30
use Koha::Patrons;
31
32
use t::lib::Mocks;
33
use t::lib::TestBuilder;
34
35
my $schema = Koha::Database->new->schema;
36
$schema->storage->dbh->{PrintError} = 0;
37
my $builder = t::lib::TestBuilder->new;
38
C4::Context->interface('commandline');
39
40
subtest 'add_print_notice_charge_if_needed with charging disabled' => sub {
41
    plan tests => 3;
42
43
    $schema->storage->txn_begin;
44
45
    # Create category with print notice charging disabled (0.00)
46
    my $category =
47
        $builder->build_object( { class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.00 } } );
48
    my $patron =
49
        $builder->build_object( { class => 'Koha::Patrons', value => { categorycode => $category->categorycode } } );
50
    my $account = Koha::Account->new( { patron_id => $patron->borrowernumber } );
51
52
    my $initial_balance = $account->balance;
53
54
    my $result = $patron->add_print_notice_charge_if_needed(
55
        {
56
            notice_code => 'ODUE',
57
            library_id  => $patron->branchcode
58
        }
59
    );
60
61
    is( $result,                undef,            'No charge applied when charging disabled' );
62
    is( $account->balance,      $initial_balance, 'Account balance unchanged' );
63
    is( $account->lines->count, 0,                'No account lines created' );
64
65
    $schema->storage->txn_rollback;
66
};
67
68
subtest 'add_print_notice_charge_if_needed with charging enabled' => sub {
69
    plan tests => 7;
70
71
    $schema->storage->txn_begin;
72
73
    # Create category with print notice charging enabled (0.75)
74
    my $category =
75
        $builder->build_object( { class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.75 } } );
76
    my $patron =
77
        $builder->build_object( { class => 'Koha::Patrons', value => { categorycode => $category->categorycode } } );
78
    my $account = Koha::Account->new( { patron_id => $patron->borrowernumber } );
79
80
    my $initial_balance = $account->balance;
81
82
    my $result = $patron->add_print_notice_charge_if_needed(
83
        {
84
            notice_code => 'ODUE',
85
            library_id  => $patron->branchcode
86
        }
87
    );
88
89
    isa_ok( $result, 'Koha::Account::Line', 'Returns account line object' );
90
    is( $account->balance,        $initial_balance + 0.75, 'Account balance increased by charge amount' );
91
    is( $result->debit_type_code, 'PRINT_NOTICE',          'Correct debit type applied' );
92
    cmp_ok( $result->amount,            '==', 0.75, 'Correct amount charged' );
93
    cmp_ok( $result->amountoutstanding, '==', 0.75, 'Full amount outstanding' );
94
    is( $result->borrowernumber, $patron->borrowernumber, 'Correct patron charged' );
95
    is( $result->interface,      'commandline',           'Correct interface recorded' );
96
97
    $schema->storage->txn_rollback;
98
};
99
100
subtest 'add_print_notice_charge_if_needed with custom amount' => sub {
101
    plan tests => 3;
102
103
    $schema->storage->txn_begin;
104
105
    # Create category with print notice charging enabled (0.50)
106
    my $category =
107
        $builder->build_object( { class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.50 } } );
108
    my $patron =
109
        $builder->build_object( { class => 'Koha::Patrons', value => { categorycode => $category->categorycode } } );
110
    my $account = Koha::Account->new( { patron_id => $patron->borrowernumber } );
111
112
    my $custom_amount = 1.25;
113
    my $result        = $patron->add_print_notice_charge_if_needed(
114
        {
115
            notice_code => 'PREDUE',
116
            library_id  => $patron->branchcode,
117
            amount      => $custom_amount
118
        }
119
    );
120
121
    isa_ok( $result, 'Koha::Account::Line', 'Returns account line object' );
122
    is( $result->amount,   $custom_amount, 'Custom amount used instead of system preference' );
123
    is( $account->balance, $custom_amount, 'Account balance reflects custom amount' );
124
125
    $schema->storage->txn_rollback;
126
};
127
128
subtest 'add_print_notice_charge_if_needed with zero amount' => sub {
129
    plan tests => 3;
130
131
    $schema->storage->txn_begin;
132
133
    # Create category with charging disabled (0.00)
134
    my $category =
135
        $builder->build_object( { class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.00 } } );
136
    my $patron =
137
        $builder->build_object( { class => 'Koha::Patrons', value => { categorycode => $category->categorycode } } );
138
    my $account = Koha::Account->new( { patron_id => $patron->borrowernumber } );
139
140
    my $result = $patron->add_print_notice_charge_if_needed(
141
        {
142
            notice_code => 'HOLD',
143
            library_id  => $patron->branchcode
144
        }
145
    );
146
147
    is( $result,                undef, 'No charge applied when amount is zero' );
148
    is( $account->balance,      0,     'Account balance unchanged' );
149
    is( $account->lines->count, 0,     'No account lines created' );
150
151
    $schema->storage->txn_rollback;
152
};
153
154
subtest 'add_print_notice_charge_if_needed validation tests' => sub {
155
    plan tests => 3;
156
157
    $schema->storage->txn_begin;
158
159
    # Create category with print notice charging enabled
160
    my $category =
161
        $builder->build_object( { class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.50 } } );
162
    my $patron =
163
        $builder->build_object( { class => 'Koha::Patrons', value => { categorycode => $category->categorycode } } );
164
165
    # Test with invalid negative amount
166
    my $result1 = $patron->add_print_notice_charge_if_needed(
167
        {
168
            notice_code => 'ODUE',
169
            library_id  => $patron->branchcode,
170
            amount      => -0.50
171
        }
172
    );
173
    is( $result1, undef, 'No charge applied for negative amount' );
174
175
    # Test with invalid non-numeric amount
176
    my $result2 = $patron->add_print_notice_charge_if_needed(
177
        {
178
            notice_code => 'ODUE',
179
            library_id  => $patron->branchcode,
180
            amount      => 'invalid'
181
        }
182
    );
183
    is( $result2, undef, 'No charge applied for non-numeric amount' );
184
185
    # Test with valid amount works
186
    my $result3 = $patron->add_print_notice_charge_if_needed(
187
        {
188
            notice_code => 'ODUE',
189
            library_id  => $patron->branchcode,
190
            amount      => 1.00
191
        }
192
    );
193
    isa_ok( $result3, 'Koha::Account::Line', 'Valid amount works correctly' );
194
195
    $schema->storage->txn_rollback;
196
};
197
198
subtest 'add_print_notice_charge_if_needed without notice code' => sub {
199
    plan tests => 2;
200
201
    $schema->storage->txn_begin;
202
203
    # Create category with print notice charging enabled (0.50)
204
    my $category =
205
        $builder->build_object( { class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.50 } } );
206
    my $patron =
207
        $builder->build_object( { class => 'Koha::Patrons', value => { categorycode => $category->categorycode } } );
208
    my $account = Koha::Account->new( { patron_id => $patron->borrowernumber } );
209
210
    my $result = $patron->add_print_notice_charge_if_needed( { library_id => $patron->branchcode } );
211
212
    isa_ok( $result, 'Koha::Account::Line', 'Returns account line object even without notice code' );
213
    cmp_ok( $result->amount, '==', 0.50, 'Category amount used' );
214
215
    $schema->storage->txn_rollback;
216
};
217
218
subtest 'add_print_notice_charge_if_needed library_id handling' => sub {
219
    plan tests => 4;
220
221
    $schema->storage->txn_begin;
222
223
    # Create category with print notice charging enabled (0.50)
224
    my $category =
225
        $builder->build_object( { class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.50 } } );
226
    my $patron =
227
        $builder->build_object( { class => 'Koha::Patrons', value => { categorycode => $category->categorycode } } );
228
    my $account = Koha::Account->new( { patron_id => $patron->borrowernumber } );
229
230
    # Get the patron's library for testing
231
    my $patron_library = $patron->branchcode;
232
233
    # Mock userenv for default library
234
    my $mock_context = Test::MockModule->new('C4::Context');
235
    $mock_context->mock(
236
        'userenv',
237
        sub {
238
            return { branch => $patron_library };
239
        }
240
    );
241
242
    # Test with explicit library_id
243
    my $result1 = $patron->add_print_notice_charge_if_needed(
244
        {
245
            notice_code => 'ODUE',
246
            library_id  => $patron_library
247
        }
248
    );
249
    is( $result1->branchcode, $patron_library, 'Explicit library_id used when provided' );
250
251
    # Test without library_id (should use userenv)
252
    my $result2 = $patron->add_print_notice_charge_if_needed( { notice_code => 'PREDUE' } );
253
    is( $result2->branchcode, $patron_library, 'Default library from userenv used when not provided' );
254
255
    # Test with undefined userenv
256
    $mock_context->mock( 'userenv', sub { return; } );
257
    my $result3 = $patron->add_print_notice_charge_if_needed( { notice_code => 'HOLD' } );
258
    isa_ok( $result3, 'Koha::Account::Line', 'Still works with undefined userenv' );
259
    is( $result3->branchcode, undef, 'Library_id is undef when no userenv' );
260
261
    $schema->storage->txn_rollback;
262
};
263
264
subtest 'add_print_notice_charge_if_needed category isolation test' => sub {
265
    plan tests => 6;
266
267
    $schema->storage->txn_begin;
268
269
    # Create two categories - one with charging enabled, one disabled
270
    my $category_enabled =
271
        $builder->build_object( { class => 'Koha::Patron::Categories', value => { print_notice_charge => 1.25 } } );
272
    my $category_disabled =
273
        $builder->build_object( { class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.00 } } );
274
275
    # Create patrons in each category
276
    my $patron_enabled = $builder->build_object(
277
        { class => 'Koha::Patrons', value => { categorycode => $category_enabled->categorycode } } );
278
    my $patron_disabled = $builder->build_object(
279
        { class => 'Koha::Patrons', value => { categorycode => $category_disabled->categorycode } } );
280
281
    my $account_enabled  = Koha::Account->new( { patron_id => $patron_enabled->borrowernumber } );
282
    my $account_disabled = Koha::Account->new( { patron_id => $patron_disabled->borrowernumber } );
283
284
    # Test patron with enabled category gets charged
285
    my $result_enabled = $patron_enabled->add_print_notice_charge_if_needed(
286
        {
287
            notice_code => 'ODUE',
288
            library_id  => $patron_enabled->branchcode
289
        }
290
    );
291
292
    isa_ok( $result_enabled, 'Koha::Account::Line', 'Patron with enabled category gets charged' );
293
    cmp_ok( $result_enabled->amount,   '==', 1.25, 'Correct amount from enabled category' );
294
    cmp_ok( $account_enabled->balance, '==', 1.25, 'Enabled patron account balance updated' );
295
296
    # Test patron with disabled category does not get charged
297
    my $result_disabled = $patron_disabled->add_print_notice_charge_if_needed(
298
        {
299
            notice_code => 'ODUE',
300
            library_id  => $patron_disabled->branchcode
301
        }
302
    );
303
304
    is( $result_disabled, undef, 'Patron with disabled category not charged' );
305
    cmp_ok( $account_disabled->balance, '==', 0, 'Disabled patron account balance unchanged' );
306
    is( $account_disabled->lines->count, 0, 'No account lines for disabled category patron' );
307
308
    $schema->storage->txn_rollback;
309
};
(-)a/t/db_dependent/PrintNoticeCharging_EndToEnd.t (-156 / +196 lines)
Lines 43-94 subtest 'print notice charging workflow with charging enabled' => sub { Link Here
43
    $schema->storage->txn_begin;
43
    $schema->storage->txn_begin;
44
44
45
    # Create category with print notice charging enabled (1.00)
45
    # Create category with print notice charging enabled (1.00)
46
    my $category = $builder->build_object({ class => 'Koha::Patron::Categories', value => { print_notice_charge => 1.00 } });
46
    my $category =
47
        $builder->build_object( { class => 'Koha::Patron::Categories', value => { print_notice_charge => 1.00 } } );
47
48
48
    # Create test patron and library
49
    # Create test patron and library
49
    my $library = $builder->build_object({ class => 'Koha::Libraries' });
50
    my $library = $builder->build_object( { class => 'Koha::Libraries' } );
50
    my $patron = $builder->build_object({
51
    my $patron  = $builder->build_object(
51
        class => 'Koha::Patrons',
52
        {
52
        value => {
53
            class => 'Koha::Patrons',
53
            branchcode => $library->branchcode,
54
            value => {
54
            categorycode => $category->categorycode
55
                branchcode   => $library->branchcode,
56
                categorycode => $category->categorycode
57
            }
55
        }
58
        }
56
    });
59
    );
57
60
58
    # Create a print notice in message queue
61
    # Create a print notice in message queue
59
    my $message = Koha::Notice::Message->new({
62
    my $message = Koha::Notice::Message->new(
60
        borrowernumber => $patron->borrowernumber,
63
        {
61
        subject => 'Test Notice',
64
            borrowernumber         => $patron->borrowernumber,
62
        content => 'This is a test print notice',
65
            subject                => 'Test Notice',
63
        message_transport_type => 'print',
66
            content                => 'This is a test print notice',
64
        status => 'pending',
67
            message_transport_type => 'print',
65
        letter_code => 'ODUE',
68
            status                 => 'pending',
66
        to_address => $patron->address,
69
            letter_code            => 'ODUE',
67
    })->store();
70
            to_address             => $patron->address,
71
        }
72
    )->store();
68
73
69
    my $account = $patron->account;
74
    my $account         = $patron->account;
70
    my $initial_balance = $account->balance;
75
    my $initial_balance = $account->balance;
71
    my $initial_lines = $account->lines->count;
76
    my $initial_lines   = $account->lines->count;
72
77
73
    # Simulate what gather_print_notices.pl does
78
    # Simulate what gather_print_notices.pl does
74
    is($message->status, 'pending', 'Message starts as pending');
79
    is( $message->status, 'pending', 'Message starts as pending' );
75
80
76
    # Apply print notice charge (this is what our enhanced gather_print_notices.pl does)
81
    # Apply print notice charge (this is what our enhanced gather_print_notices.pl does)
77
    my $charge_result = $patron->add_print_notice_charge_if_needed({
82
    my $charge_result = $patron->add_print_notice_charge_if_needed(
78
        notice_code => $message->letter_code,
83
        {
79
        library_id => $library->branchcode,
84
            notice_code => $message->letter_code,
80
    });
85
            library_id  => $library->branchcode,
86
        }
87
    );
81
88
82
    # Update message status to sent (as gather_print_notices.pl would do)
89
    # Update message status to sent (as gather_print_notices.pl would do)
83
    $message->status('sent')->store();
90
    $message->status('sent')->store();
84
91
85
    # Verify the charge was applied
92
    # Verify the charge was applied
86
    isa_ok($charge_result, 'Koha::Account::Line', 'Charge was created');
93
    isa_ok( $charge_result, 'Koha::Account::Line', 'Charge was created' );
87
    is($account->balance, $initial_balance + 1.00, 'Account balance increased by charge amount');
94
    is( $account->balance,               $initial_balance + 1.00, 'Account balance increased by charge amount' );
88
    is($account->lines->count, $initial_lines + 1, 'New account line created');
95
    is( $account->lines->count,          $initial_lines + 1,      'New account line created' );
89
    is($charge_result->debit_type_code, 'PRINT_NOTICE', 'Correct debit type');
96
    is( $charge_result->debit_type_code, 'PRINT_NOTICE',          'Correct debit type' );
90
    is($charge_result->borrowernumber, $patron->borrowernumber, 'Correct patron charged');
97
    is( $charge_result->borrowernumber,  $patron->borrowernumber, 'Correct patron charged' );
91
    is($message->status, 'sent', 'Message marked as sent');
98
    is( $message->status,                'sent',                  'Message marked as sent' );
92
99
93
    $schema->storage->txn_rollback;
100
    $schema->storage->txn_rollback;
94
};
101
};
Lines 99-143 subtest 'print notice charging workflow with charging disabled' => sub { Link Here
99
    $schema->storage->txn_begin;
106
    $schema->storage->txn_begin;
100
107
101
    # Create category with print notice charging disabled (0.00)
108
    # Create category with print notice charging disabled (0.00)
102
    my $category = $builder->build_object({ class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.00 } });
109
    my $category =
110
        $builder->build_object( { class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.00 } } );
103
111
104
    # Create test patron and library
112
    # Create test patron and library
105
    my $library = $builder->build_object({ class => 'Koha::Libraries' });
113
    my $library = $builder->build_object( { class => 'Koha::Libraries' } );
106
    my $patron = $builder->build_object({
114
    my $patron  = $builder->build_object(
107
        class => 'Koha::Patrons',
115
        {
108
        value => {
116
            class => 'Koha::Patrons',
109
            branchcode => $library->branchcode,
117
            value => {
110
            categorycode => $category->categorycode
118
                branchcode   => $library->branchcode,
119
                categorycode => $category->categorycode
120
            }
111
        }
121
        }
112
    });
122
    );
113
123
114
    # Create a print notice in message queue
124
    # Create a print notice in message queue
115
    my $message = Koha::Notice::Message->new({
125
    my $message = Koha::Notice::Message->new(
116
        borrowernumber => $patron->borrowernumber,
126
        {
117
        subject => 'Test Notice',
127
            borrowernumber         => $patron->borrowernumber,
118
        content => 'This is a test print notice',
128
            subject                => 'Test Notice',
119
        message_transport_type => 'print',
129
            content                => 'This is a test print notice',
120
        status => 'pending',
130
            message_transport_type => 'print',
121
        letter_code => 'ODUE',
131
            status                 => 'pending',
122
        to_address => $patron->address,
132
            letter_code            => 'ODUE',
123
    })->store();
133
            to_address             => $patron->address,
134
        }
135
    )->store();
124
136
125
    my $account = $patron->account;
137
    my $account         = $patron->account;
126
    my $initial_balance = $account->balance;
138
    my $initial_balance = $account->balance;
127
    my $initial_lines = $account->lines->count;
139
    my $initial_lines   = $account->lines->count;
128
140
129
    # Simulate what gather_print_notices.pl does when charging disabled
141
    # Simulate what gather_print_notices.pl does when charging disabled
130
    my $charge_result = $patron->add_print_notice_charge_if_needed({
142
    my $charge_result = $patron->add_print_notice_charge_if_needed(
131
        notice_code => $message->letter_code,
143
        {
132
        library_id => $library->branchcode,
144
            notice_code => $message->letter_code,
133
    });
145
            library_id  => $library->branchcode,
146
        }
147
    );
134
    $message->status('sent')->store();
148
    $message->status('sent')->store();
135
149
136
    # Verify no charge was applied
150
    # Verify no charge was applied
137
    is($charge_result, undef, 'No charge created when charging disabled');
151
    is( $charge_result,         undef,            'No charge created when charging disabled' );
138
    is($account->balance, $initial_balance, 'Account balance unchanged');
152
    is( $account->balance,      $initial_balance, 'Account balance unchanged' );
139
    is($account->lines->count, $initial_lines, 'No new account lines created');
153
    is( $account->lines->count, $initial_lines,   'No new account lines created' );
140
    is($message->status, 'sent', 'Message still marked as sent');
154
    is( $message->status,       'sent',           'Message still marked as sent' );
141
155
142
    $schema->storage->txn_rollback;
156
    $schema->storage->txn_rollback;
143
};
157
};
Lines 148-195 subtest 'multiple print notices for same patron' => sub { Link Here
148
    $schema->storage->txn_begin;
162
    $schema->storage->txn_begin;
149
163
150
    # Create category with print notice charging enabled (0.50)
164
    # Create category with print notice charging enabled (0.50)
151
    my $category = $builder->build_object({ class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.50 } });
165
    my $category =
166
        $builder->build_object( { class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.50 } } );
152
167
153
    # Create test patron
168
    # Create test patron
154
    my $patron = $builder->build_object({
169
    my $patron = $builder->build_object(
155
        class => 'Koha::Patrons',
170
        {
156
        value => { categorycode => $category->categorycode }
171
            class => 'Koha::Patrons',
157
    });
172
            value => { categorycode => $category->categorycode }
173
        }
174
    );
158
    my $account = $patron->account;
175
    my $account = $patron->account;
159
176
160
    # Create multiple print notices
177
    # Create multiple print notices
161
    my @notice_codes = ('ODUE', 'PREDUE', 'HOLD');
178
    my @notice_codes = ( 'ODUE', 'PREDUE', 'HOLD' );
162
    my @charges;
179
    my @charges;
163
180
164
    foreach my $code (@notice_codes) {
181
    foreach my $code (@notice_codes) {
165
        my $message = Koha::Notice::Message->new({
182
        my $message = Koha::Notice::Message->new(
166
            borrowernumber => $patron->borrowernumber,
183
            {
167
            subject => "Test $code Notice",
184
                borrowernumber         => $patron->borrowernumber,
168
            content => "This is a test $code print notice",
185
                subject                => "Test $code Notice",
169
            message_transport_type => 'print',
186
                content                => "This is a test $code print notice",
170
            status => 'pending',
187
                message_transport_type => 'print',
171
            letter_code => $code,
188
                status                 => 'pending',
172
            to_address => $patron->address,
189
                letter_code            => $code,
173
        })->store();
190
                to_address             => $patron->address,
191
            }
192
        )->store();
174
193
175
        # Apply charge for each notice
194
        # Apply charge for each notice
176
        my $charge = $patron->add_print_notice_charge_if_needed({
195
        my $charge = $patron->add_print_notice_charge_if_needed(
177
            notice_code => $code,
196
            {
178
            library_id => $patron->branchcode,
197
                notice_code => $code,
179
        });
198
                library_id  => $patron->branchcode,
199
            }
200
        );
180
        push @charges, $charge;
201
        push @charges, $charge;
181
        $message->status('sent')->store();
202
        $message->status('sent')->store();
182
    }
203
    }
183
204
184
    # Verify all charges were applied
205
    # Verify all charges were applied
185
    is(scalar @charges, 3, 'Three charges created');
206
    is( scalar @charges,        3,    'Three charges created' );
186
    is($account->balance, 1.50, 'Total balance is 3 × $0.50 = $1.50');
207
    is( $account->balance,      1.50, 'Total balance is 3 × $0.50 = $1.50' );
187
    is($account->lines->count, 3, 'Three account lines created');
208
    is( $account->lines->count, 3,    'Three account lines created' );
188
209
189
    # Verify each charge has correct debit type
210
    # Verify each charge has correct debit type
190
    my @lines = $account->lines->as_list;
211
    my @lines       = $account->lines->as_list;
191
    my @debit_types = map { $_->debit_type_code } @lines;
212
    my @debit_types = map { $_->debit_type_code } @lines;
192
    is(scalar(grep { $_ eq 'PRINT_NOTICE' } @debit_types), 3, 'All charges have PRINT_NOTICE debit type');
213
    is( scalar( grep { $_ eq 'PRINT_NOTICE' } @debit_types ), 3, 'All charges have PRINT_NOTICE debit type' );
193
214
194
    $schema->storage->txn_rollback;
215
    $schema->storage->txn_rollback;
195
};
216
};
Lines 200-247 subtest 'print notice charging with missing patron data' => sub { Link Here
200
    $schema->storage->txn_begin;
221
    $schema->storage->txn_begin;
201
222
202
    # Create category with print notice charging enabled (1.00)
223
    # Create category with print notice charging enabled (1.00)
203
    my $category = $builder->build_object({ class => 'Koha::Patron::Categories', value => { print_notice_charge => 1.00 } });
224
    my $category =
225
        $builder->build_object( { class => 'Koha::Patron::Categories', value => { print_notice_charge => 1.00 } } );
204
226
205
    # Create a patron then delete it to simulate missing data
227
    # Create a patron then delete it to simulate missing data
206
    my $patron = $builder->build_object({
228
    my $patron = $builder->build_object(
207
        class => 'Koha::Patrons',
229
        {
208
        value => { categorycode => $category->categorycode }
230
            class => 'Koha::Patrons',
209
    });
231
            value => { categorycode => $category->categorycode }
232
        }
233
    );
210
    my $borrowernumber = $patron->borrowernumber;
234
    my $borrowernumber = $patron->borrowernumber;
211
    $patron->delete();
235
    $patron->delete();
212
236
213
    # Create a message with reference to deleted patron
237
    # Create a message with reference to deleted patron
214
    my $message = {
238
    my $message = {
215
        borrowernumber => $borrowernumber,
239
        borrowernumber => $borrowernumber,
216
        letter_code => 'ODUE',
240
        letter_code    => 'ODUE',
217
    };
241
    };
218
242
219
    # Test our apply_print_notice_charge function behavior
243
    # Test our apply_print_notice_charge function behavior
220
    # This simulates what would happen in gather_print_notices.pl
244
    # This simulates what would happen in gather_print_notices.pl
221
    my $found_patron = Koha::Patrons->find($borrowernumber);
245
    my $found_patron = Koha::Patrons->find($borrowernumber);
222
    is($found_patron, undef, 'Patron not found as expected');
246
    is( $found_patron, undef, 'Patron not found as expected' );
223
247
224
    # The function should handle missing patrons gracefully
248
    # The function should handle missing patrons gracefully
225
    my $result;
249
    my $result;
226
    warnings_are {
250
    warnings_are {
227
        if ($found_patron) {
251
        if ($found_patron) {
228
            my $account = Koha::Account->new({ patron_id => $borrowernumber });
252
            my $account = Koha::Account->new( { patron_id => $borrowernumber } );
229
            $result = $patron->add_print_notice_charge_if_needed({
253
            $result = $patron->add_print_notice_charge_if_needed(
230
                notice_code => $message->{letter_code},
254
                {
231
                library_id => 'CPL',  # Use a default library for this test
255
                    notice_code => $message->{letter_code},
232
            });
256
                    library_id  => 'CPL',                     # Use a default library for this test
257
                }
258
            );
233
        } else {
259
        } else {
234
            $result = undef;
260
            $result = undef;
235
        }
261
        }
236
    } [], 'No warnings when patron not found';
262
    }
263
    [], 'No warnings when patron not found';
237
264
238
    is($result, undef, 'No charge attempted for missing patron');
265
    is( $result, undef, 'No charge attempted for missing patron' );
239
266
240
    # Verify no orphaned account lines
267
    # Verify no orphaned account lines
241
    my $orphaned_lines = Koha::Account::Lines->search({
268
    my $orphaned_lines = Koha::Account::Lines->search( { borrowernumber => $borrowernumber } );
242
        borrowernumber => $borrowernumber
269
    is( $orphaned_lines->count, 0, 'No orphaned account lines created' );
243
    });
244
    is($orphaned_lines->count, 0, 'No orphaned account lines created');
245
270
246
    $schema->storage->txn_rollback;
271
    $schema->storage->txn_rollback;
247
};
272
};
Lines 252-299 subtest 'print notice charging with different amounts' => sub { Link Here
252
    $schema->storage->txn_begin;
277
    $schema->storage->txn_begin;
253
278
254
    # Create category with print notice charging enabled (0.75)
279
    # Create category with print notice charging enabled (0.75)
255
    my $category = $builder->build_object({ class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.75 } });
280
    my $category =
281
        $builder->build_object( { class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.75 } } );
256
282
257
    my $patron = $builder->build_object({
283
    my $patron = $builder->build_object(
258
        class => 'Koha::Patrons',
284
        {
259
        value => { categorycode => $category->categorycode }
285
            class => 'Koha::Patrons',
260
    });
286
            value => { categorycode => $category->categorycode }
287
        }
288
    );
261
    my $account = $patron->account;
289
    my $account = $patron->account;
262
290
263
    # Test 1: Use system preference amount
291
    # Test 1: Use system preference amount
264
    my $charge1 = $patron->add_print_notice_charge_if_needed({
292
    my $charge1 = $patron->add_print_notice_charge_if_needed(
265
        notice_code => 'ODUE',
293
        {
266
        library_id => $patron->branchcode,
294
            notice_code => 'ODUE',
267
    });
295
            library_id  => $patron->branchcode,
268
    cmp_ok($charge1->amount, '==', 0.75, 'Category amount used when no custom amount');
296
        }
297
    );
298
    cmp_ok( $charge1->amount, '==', 0.75, 'Category amount used when no custom amount' );
269
299
270
    # Test 2: Use custom amount
300
    # Test 2: Use custom amount
271
    my $charge2 = $patron->add_print_notice_charge_if_needed({
301
    my $charge2 = $patron->add_print_notice_charge_if_needed(
272
        notice_code => 'PREDUE',
302
        {
273
        library_id => $patron->branchcode,
303
            notice_code => 'PREDUE',
274
        amount => 1.50,
304
            library_id  => $patron->branchcode,
275
    });
305
            amount      => 1.50,
276
    is($charge2->amount, 1.50, 'Custom amount used when provided');
306
        }
307
    );
308
    is( $charge2->amount, 1.50, 'Custom amount used when provided' );
277
309
278
    # Test 3: Zero custom amount should not create charge
310
    # Test 3: Zero custom amount should not create charge
279
    my $charge3 = $patron->add_print_notice_charge_if_needed({
311
    my $charge3 = $patron->add_print_notice_charge_if_needed(
280
        notice_code => 'HOLD',
312
        {
281
        library_id => $patron->branchcode,
313
            notice_code => 'HOLD',
282
        amount => 0.00,
314
            library_id  => $patron->branchcode,
283
    });
315
            amount      => 0.00,
284
    is($charge3, undef, 'No charge created for zero amount');
316
        }
317
    );
318
    is( $charge3, undef, 'No charge created for zero amount' );
285
319
286
    # Test 4: Very small amount
320
    # Test 4: Very small amount
287
    my $charge4 = $patron->add_print_notice_charge_if_needed({
321
    my $charge4 = $patron->add_print_notice_charge_if_needed(
288
        notice_code => 'RENEWAL',
322
        {
289
        library_id => $patron->branchcode,
323
            notice_code => 'RENEWAL',
290
        amount => 0.01,
324
            library_id  => $patron->branchcode,
291
    });
325
            amount      => 0.01,
292
    is($charge4->amount, 0.01, 'Very small amounts work correctly');
326
        }
327
    );
328
    is( $charge4->amount, 0.01, 'Very small amounts work correctly' );
293
329
294
    # Verify total balance
330
    # Verify total balance
295
    is($account->balance, 2.26, 'Total balance is 0.75 + 1.50 + 0.01 = 2.26');
331
    is( $account->balance,      2.26, 'Total balance is 0.75 + 1.50 + 0.01 = 2.26' );
296
    is($account->lines->count, 3, 'Three charges created (zero amount not counted)');
332
    is( $account->lines->count, 3,    'Three charges created (zero amount not counted)' );
297
333
298
    $schema->storage->txn_rollback;
334
    $schema->storage->txn_rollback;
299
};
335
};
Lines 304-322 subtest 'gather_print_notices.pl helper function tests' => sub { Link Here
304
    $schema->storage->txn_begin;
340
    $schema->storage->txn_begin;
305
341
306
    # Create category with print notice charging enabled (0.60)
342
    # Create category with print notice charging enabled (0.60)
307
    my $category = $builder->build_object({ class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.60 } });
343
    my $category =
344
        $builder->build_object( { class => 'Koha::Patron::Categories', value => { print_notice_charge => 0.60 } } );
308
345
309
    # Create test patron
346
    # Create test patron
310
    my $patron = $builder->build_object({
347
    my $patron = $builder->build_object(
311
        class => 'Koha::Patrons',
348
        {
312
        value => { categorycode => $category->categorycode }
349
            class => 'Koha::Patrons',
313
    });
350
            value => { categorycode => $category->categorycode }
351
        }
352
    );
314
    my $account = $patron->account;
353
    my $account = $patron->account;
315
354
316
    # Test valid message hash
355
    # Test valid message hash
317
    my $valid_message = {
356
    my $valid_message = {
318
        borrowernumber => $patron->borrowernumber,
357
        borrowernumber => $patron->borrowernumber,
319
        letter_code => 'ODUE',
358
        letter_code    => 'ODUE',
320
    };
359
    };
321
360
322
    # Simulate the apply_print_notice_charge function from gather_print_notices.pl
361
    # Simulate the apply_print_notice_charge function from gather_print_notices.pl
Lines 325-339 subtest 'gather_print_notices.pl helper function tests' => sub { Link Here
325
364
326
        return unless $message->{borrowernumber};
365
        return unless $message->{borrowernumber};
327
366
328
        my $patron = Koha::Patrons->find($message->{borrowernumber});
367
        my $patron = Koha::Patrons->find( $message->{borrowernumber} );
329
        return unless $patron;
368
        return unless $patron;
330
369
331
        eval {
370
        eval {
332
            my $account = Koha::Account->new({ patron_id => $patron->borrowernumber });
371
            my $account = Koha::Account->new( { patron_id => $patron->borrowernumber } );
333
            $patron->add_print_notice_charge_if_needed({
372
            $patron->add_print_notice_charge_if_needed(
334
                notice_code => $message->{letter_code},
373
                {
335
                library_id  => $patron->branchcode,
374
                    notice_code => $message->{letter_code},
336
            });
375
                    library_id  => $patron->branchcode,
376
                }
377
            );
337
        };
378
        };
338
379
339
        if ($@) {
380
        if ($@) {
Lines 345-386 subtest 'gather_print_notices.pl helper function tests' => sub { Link Here
345
386
346
    # Test 1: Valid message
387
    # Test 1: Valid message
347
    my $result1 = $apply_charge->($valid_message);
388
    my $result1 = $apply_charge->($valid_message);
348
    is($result1, 1, 'Valid message processed successfully');
389
    is( $result1,          1,    'Valid message processed successfully' );
349
    is($account->balance, 0.60, 'Charge applied correctly');
390
    is( $account->balance, 0.60, 'Charge applied correctly' );
350
391
351
    # Test 2: Message with missing borrowernumber
392
    # Test 2: Message with missing borrowernumber
352
    my $invalid_message1 = {
393
    my $invalid_message1 = {
353
        letter_code => 'ODUE',
394
        letter_code => 'ODUE',
354
        branchcode => $patron->branchcode,
395
        branchcode  => $patron->branchcode,
355
    };
396
    };
356
    my $result2 = $apply_charge->($invalid_message1);
397
    my $result2 = $apply_charge->($invalid_message1);
357
    is($result2, undef, 'Message without borrowernumber handled gracefully');
398
    is( $result2, undef, 'Message without borrowernumber handled gracefully' );
358
399
359
    # Test 3: Message with invalid borrowernumber
400
    # Test 3: Message with invalid borrowernumber
360
    my $invalid_message2 = {
401
    my $invalid_message2 = {
361
        borrowernumber => 999999,
402
        borrowernumber => 999999,
362
        letter_code => 'ODUE',
403
        letter_code    => 'ODUE',
363
        branchcode => $patron->branchcode,
404
        branchcode     => $patron->branchcode,
364
    };
405
    };
365
    my $result3 = $apply_charge->($invalid_message2);
406
    my $result3 = $apply_charge->($invalid_message2);
366
    is($result3, undef, 'Message with invalid borrowernumber handled gracefully');
407
    is( $result3, undef, 'Message with invalid borrowernumber handled gracefully' );
367
408
368
    # Test 4: Another valid message to same patron
409
    # Test 4: Another valid message to same patron
369
    my $valid_message2 = {
410
    my $valid_message2 = {
370
        borrowernumber => $patron->borrowernumber,
411
        borrowernumber => $patron->borrowernumber,
371
        letter_code => 'PREDUE',
412
        letter_code    => 'PREDUE',
372
        branchcode => $patron->branchcode,
413
        branchcode     => $patron->branchcode,
373
    };
414
    };
374
    my $result4 = $apply_charge->($valid_message2);
415
    my $result4 = $apply_charge->($valid_message2);
375
    is($result4, 1, 'Second valid message processed successfully');
416
    is( $result4,          1,    'Second valid message processed successfully' );
376
    is($account->balance, 1.20, 'Second charge applied correctly');
417
    is( $account->balance, 1.20, 'Second charge applied correctly' );
377
418
378
    # Test 5: Verify account lines
419
    # Test 5: Verify account lines
379
    my @lines = $account->lines->as_list;
420
    my @lines = $account->lines->as_list;
380
    is(scalar @lines, 2, 'Two account lines created');
421
    is( scalar @lines, 2, 'Two account lines created' );
381
422
382
    my @debit_types = map { $_->debit_type_code } @lines;
423
    my @debit_types = map { $_->debit_type_code } @lines;
383
    is(scalar(grep { $_ eq 'PRINT_NOTICE' } @debit_types), 2, 'Both charges have PRINT_NOTICE debit type');
424
    is( scalar( grep { $_ eq 'PRINT_NOTICE' } @debit_types ), 2, 'Both charges have PRINT_NOTICE debit type' );
384
425
385
    $schema->storage->txn_rollback;
426
    $schema->storage->txn_rollback;
386
};
427
};
387
- 

Return to bug 4858