Line 0
Link Here
|
0 |
- |
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 => 6; |
23 |
use Test::Warn; |
24 |
|
25 |
use C4::Context; |
26 |
use C4::Letters; |
27 |
use Koha::Account; |
28 |
use Koha::Account::Lines; |
29 |
use Koha::Patrons; |
30 |
use Koha::Notice::Messages; |
31 |
use Koha::Libraries; |
32 |
|
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 'print notice charging workflow with charging enabled' => sub { |
41 |
plan tests => 7; |
42 |
|
43 |
$schema->storage->txn_begin; |
44 |
|
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 } }); |
47 |
|
48 |
# Create test patron and library |
49 |
my $library = $builder->build_object({ class => 'Koha::Libraries' }); |
50 |
my $patron = $builder->build_object({ |
51 |
class => 'Koha::Patrons', |
52 |
value => { |
53 |
branchcode => $library->branchcode, |
54 |
categorycode => $category->categorycode |
55 |
} |
56 |
}); |
57 |
|
58 |
# Create a print notice in message queue |
59 |
my $message = Koha::Notice::Message->new({ |
60 |
borrowernumber => $patron->borrowernumber, |
61 |
subject => 'Test Notice', |
62 |
content => 'This is a test print notice', |
63 |
message_transport_type => 'print', |
64 |
status => 'pending', |
65 |
letter_code => 'ODUE', |
66 |
to_address => $patron->address, |
67 |
})->store(); |
68 |
|
69 |
my $account = $patron->account; |
70 |
my $initial_balance = $account->balance; |
71 |
my $initial_lines = $account->lines->count; |
72 |
|
73 |
# Simulate what gather_print_notices.pl does |
74 |
is($message->status, 'pending', 'Message starts as pending'); |
75 |
|
76 |
# 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({ |
78 |
notice_code => $message->letter_code, |
79 |
library_id => $library->branchcode, |
80 |
}); |
81 |
|
82 |
# Update message status to sent (as gather_print_notices.pl would do) |
83 |
$message->status('sent')->store(); |
84 |
|
85 |
# Verify the charge was applied |
86 |
isa_ok($charge_result, 'Koha::Account::Line', 'Charge was created'); |
87 |
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'); |
89 |
is($charge_result->debit_type_code, 'PRINT_NOTICE', 'Correct debit type'); |
90 |
is($charge_result->borrowernumber, $patron->borrowernumber, 'Correct patron charged'); |
91 |
is($message->status, 'sent', 'Message marked as sent'); |
92 |
|
93 |
$schema->storage->txn_rollback; |
94 |
}; |
95 |
|
96 |
subtest 'print notice charging workflow with charging disabled' => sub { |
97 |
plan tests => 4; |
98 |
|
99 |
$schema->storage->txn_begin; |
100 |
|
101 |
# 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 } }); |
103 |
|
104 |
# Create test patron and library |
105 |
my $library = $builder->build_object({ class => 'Koha::Libraries' }); |
106 |
my $patron = $builder->build_object({ |
107 |
class => 'Koha::Patrons', |
108 |
value => { |
109 |
branchcode => $library->branchcode, |
110 |
categorycode => $category->categorycode |
111 |
} |
112 |
}); |
113 |
|
114 |
# Create a print notice in message queue |
115 |
my $message = Koha::Notice::Message->new({ |
116 |
borrowernumber => $patron->borrowernumber, |
117 |
subject => 'Test Notice', |
118 |
content => 'This is a test print notice', |
119 |
message_transport_type => 'print', |
120 |
status => 'pending', |
121 |
letter_code => 'ODUE', |
122 |
to_address => $patron->address, |
123 |
})->store(); |
124 |
|
125 |
my $account = $patron->account; |
126 |
my $initial_balance = $account->balance; |
127 |
my $initial_lines = $account->lines->count; |
128 |
|
129 |
# Simulate what gather_print_notices.pl does when charging disabled |
130 |
my $charge_result = $patron->add_print_notice_charge_if_needed({ |
131 |
notice_code => $message->letter_code, |
132 |
library_id => $library->branchcode, |
133 |
}); |
134 |
$message->status('sent')->store(); |
135 |
|
136 |
# Verify no charge was applied |
137 |
is($charge_result, undef, 'No charge created when charging disabled'); |
138 |
is($account->balance, $initial_balance, 'Account balance unchanged'); |
139 |
is($account->lines->count, $initial_lines, 'No new account lines created'); |
140 |
is($message->status, 'sent', 'Message still marked as sent'); |
141 |
|
142 |
$schema->storage->txn_rollback; |
143 |
}; |
144 |
|
145 |
subtest 'multiple print notices for same patron' => sub { |
146 |
plan tests => 4; |
147 |
|
148 |
$schema->storage->txn_begin; |
149 |
|
150 |
# 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 } }); |
152 |
|
153 |
# Create test patron |
154 |
my $patron = $builder->build_object({ |
155 |
class => 'Koha::Patrons', |
156 |
value => { categorycode => $category->categorycode } |
157 |
}); |
158 |
my $account = $patron->account; |
159 |
|
160 |
# Create multiple print notices |
161 |
my @notice_codes = ('ODUE', 'PREDUE', 'HOLD'); |
162 |
my @charges; |
163 |
|
164 |
foreach my $code (@notice_codes) { |
165 |
my $message = Koha::Notice::Message->new({ |
166 |
borrowernumber => $patron->borrowernumber, |
167 |
subject => "Test $code Notice", |
168 |
content => "This is a test $code print notice", |
169 |
message_transport_type => 'print', |
170 |
status => 'pending', |
171 |
letter_code => $code, |
172 |
to_address => $patron->address, |
173 |
})->store(); |
174 |
|
175 |
# Apply charge for each notice |
176 |
my $charge = $patron->add_print_notice_charge_if_needed({ |
177 |
notice_code => $code, |
178 |
library_id => $patron->branchcode, |
179 |
}); |
180 |
push @charges, $charge; |
181 |
$message->status('sent')->store(); |
182 |
} |
183 |
|
184 |
# Verify all charges were applied |
185 |
is(scalar @charges, 3, 'Three charges created'); |
186 |
is($account->balance, 1.50, 'Total balance is 3 × $0.50 = $1.50'); |
187 |
is($account->lines->count, 3, 'Three account lines created'); |
188 |
|
189 |
# Verify each charge has correct debit type |
190 |
my @lines = $account->lines->as_list; |
191 |
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'); |
193 |
|
194 |
$schema->storage->txn_rollback; |
195 |
}; |
196 |
|
197 |
subtest 'print notice charging with missing patron data' => sub { |
198 |
plan tests => 4; |
199 |
|
200 |
$schema->storage->txn_begin; |
201 |
|
202 |
# 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 } }); |
204 |
|
205 |
# Create a patron then delete it to simulate missing data |
206 |
my $patron = $builder->build_object({ |
207 |
class => 'Koha::Patrons', |
208 |
value => { categorycode => $category->categorycode } |
209 |
}); |
210 |
my $borrowernumber = $patron->borrowernumber; |
211 |
$patron->delete(); |
212 |
|
213 |
# Create a message with reference to deleted patron |
214 |
my $message = { |
215 |
borrowernumber => $borrowernumber, |
216 |
letter_code => 'ODUE', |
217 |
}; |
218 |
|
219 |
# Test our apply_print_notice_charge function behavior |
220 |
# This simulates what would happen in gather_print_notices.pl |
221 |
my $found_patron = Koha::Patrons->find($borrowernumber); |
222 |
is($found_patron, undef, 'Patron not found as expected'); |
223 |
|
224 |
# The function should handle missing patrons gracefully |
225 |
my $result; |
226 |
warnings_are { |
227 |
if ($found_patron) { |
228 |
my $account = Koha::Account->new({ patron_id => $borrowernumber }); |
229 |
$result = $patron->add_print_notice_charge_if_needed({ |
230 |
notice_code => $message->{letter_code}, |
231 |
library_id => 'CPL', # Use a default library for this test |
232 |
}); |
233 |
} else { |
234 |
$result = undef; |
235 |
} |
236 |
} [], 'No warnings when patron not found'; |
237 |
|
238 |
is($result, undef, 'No charge attempted for missing patron'); |
239 |
|
240 |
# Verify no orphaned account lines |
241 |
my $orphaned_lines = Koha::Account::Lines->search({ |
242 |
borrowernumber => $borrowernumber |
243 |
}); |
244 |
is($orphaned_lines->count, 0, 'No orphaned account lines created'); |
245 |
|
246 |
$schema->storage->txn_rollback; |
247 |
}; |
248 |
|
249 |
subtest 'print notice charging with different amounts' => sub { |
250 |
plan tests => 6; |
251 |
|
252 |
$schema->storage->txn_begin; |
253 |
|
254 |
# 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 } }); |
256 |
|
257 |
my $patron = $builder->build_object({ |
258 |
class => 'Koha::Patrons', |
259 |
value => { categorycode => $category->categorycode } |
260 |
}); |
261 |
my $account = $patron->account; |
262 |
|
263 |
# Test 1: Use system preference amount |
264 |
my $charge1 = $patron->add_print_notice_charge_if_needed({ |
265 |
notice_code => 'ODUE', |
266 |
library_id => $patron->branchcode, |
267 |
}); |
268 |
cmp_ok($charge1->amount, '==', 0.75, 'Category amount used when no custom amount'); |
269 |
|
270 |
# Test 2: Use custom amount |
271 |
my $charge2 = $patron->add_print_notice_charge_if_needed({ |
272 |
notice_code => 'PREDUE', |
273 |
library_id => $patron->branchcode, |
274 |
amount => 1.50, |
275 |
}); |
276 |
is($charge2->amount, 1.50, 'Custom amount used when provided'); |
277 |
|
278 |
# Test 3: Zero custom amount should not create charge |
279 |
my $charge3 = $patron->add_print_notice_charge_if_needed({ |
280 |
notice_code => 'HOLD', |
281 |
library_id => $patron->branchcode, |
282 |
amount => 0.00, |
283 |
}); |
284 |
is($charge3, undef, 'No charge created for zero amount'); |
285 |
|
286 |
# Test 4: Very small amount |
287 |
my $charge4 = $patron->add_print_notice_charge_if_needed({ |
288 |
notice_code => 'RENEWAL', |
289 |
library_id => $patron->branchcode, |
290 |
amount => 0.01, |
291 |
}); |
292 |
is($charge4->amount, 0.01, 'Very small amounts work correctly'); |
293 |
|
294 |
# Verify total balance |
295 |
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)'); |
297 |
|
298 |
$schema->storage->txn_rollback; |
299 |
}; |
300 |
|
301 |
subtest 'gather_print_notices.pl helper function tests' => sub { |
302 |
plan tests => 8; |
303 |
|
304 |
$schema->storage->txn_begin; |
305 |
|
306 |
# 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 } }); |
308 |
|
309 |
# Create test patron |
310 |
my $patron = $builder->build_object({ |
311 |
class => 'Koha::Patrons', |
312 |
value => { categorycode => $category->categorycode } |
313 |
}); |
314 |
my $account = $patron->account; |
315 |
|
316 |
# Test valid message hash |
317 |
my $valid_message = { |
318 |
borrowernumber => $patron->borrowernumber, |
319 |
letter_code => 'ODUE', |
320 |
}; |
321 |
|
322 |
# Simulate the apply_print_notice_charge function from gather_print_notices.pl |
323 |
my $apply_charge = sub { |
324 |
my ($message) = @_; |
325 |
|
326 |
return unless $message->{borrowernumber}; |
327 |
|
328 |
my $patron = Koha::Patrons->find($message->{borrowernumber}); |
329 |
return unless $patron; |
330 |
|
331 |
eval { |
332 |
my $account = Koha::Account->new({ patron_id => $patron->borrowernumber }); |
333 |
$patron->add_print_notice_charge_if_needed({ |
334 |
notice_code => $message->{letter_code}, |
335 |
library_id => $patron->branchcode, |
336 |
}); |
337 |
}; |
338 |
|
339 |
if ($@) { |
340 |
warn "Error applying print notice charge for patron " . $patron->borrowernumber . ": $@"; |
341 |
return; |
342 |
} |
343 |
return 1; |
344 |
}; |
345 |
|
346 |
# Test 1: Valid message |
347 |
my $result1 = $apply_charge->($valid_message); |
348 |
is($result1, 1, 'Valid message processed successfully'); |
349 |
is($account->balance, 0.60, 'Charge applied correctly'); |
350 |
|
351 |
# Test 2: Message with missing borrowernumber |
352 |
my $invalid_message1 = { |
353 |
letter_code => 'ODUE', |
354 |
branchcode => $patron->branchcode, |
355 |
}; |
356 |
my $result2 = $apply_charge->($invalid_message1); |
357 |
is($result2, undef, 'Message without borrowernumber handled gracefully'); |
358 |
|
359 |
# Test 3: Message with invalid borrowernumber |
360 |
my $invalid_message2 = { |
361 |
borrowernumber => 999999, |
362 |
letter_code => 'ODUE', |
363 |
branchcode => $patron->branchcode, |
364 |
}; |
365 |
my $result3 = $apply_charge->($invalid_message2); |
366 |
is($result3, undef, 'Message with invalid borrowernumber handled gracefully'); |
367 |
|
368 |
# Test 4: Another valid message to same patron |
369 |
my $valid_message2 = { |
370 |
borrowernumber => $patron->borrowernumber, |
371 |
letter_code => 'PREDUE', |
372 |
branchcode => $patron->branchcode, |
373 |
}; |
374 |
my $result4 = $apply_charge->($valid_message2); |
375 |
is($result4, 1, 'Second valid message processed successfully'); |
376 |
is($account->balance, 1.20, 'Second charge applied correctly'); |
377 |
|
378 |
# Test 5: Verify account lines |
379 |
my @lines = $account->lines->as_list; |
380 |
is(scalar @lines, 2, 'Two account lines created'); |
381 |
|
382 |
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'); |
384 |
|
385 |
$schema->storage->txn_rollback; |
386 |
}; |