]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/lib/OpenILS/Application/Storage/Publisher/money.pm
Post-2.5-m1 whitespace fixup
[working/Evergreen.git] / Open-ILS / src / perlmods / lib / OpenILS / Application / Storage / Publisher / money.pm
1 package OpenILS::Application::Storage::Publisher::money;
2 use base qw/OpenILS::Application::Storage/;
3 use OpenSRF::Utils::Logger qw/:level/;
4
5 my $log = 'OpenSRF::Utils::Logger';
6
7 sub _make_mbts {
8         my @xacts = @_;
9
10         my @mbts;
11         for my $x (@xacts) {
12                 my $s = new Fieldmapper::money::billable_transaction_summary;
13                 $s->id( $x->id );
14                 $s->usr( $x->usr );
15                 $s->xact_start( $x->xact_start );
16                 $s->xact_finish( $x->xact_finish );
17
18                 my $to = 0;
19                 my $lb = undef;
20                 for my $b ($x->billings) {
21                         next if ($b->voided);
22             #$log->debug( "billing is ".$b->amount, DEBUG );
23                         $to += ($b->amount * 100);
24                         $lb ||= $b->billing_ts;
25                         if ($b->billing_ts ge $lb) {
26                                 $lb = $b->billing_ts;
27                                 $s->last_billing_note($b->note);
28                                 $s->last_billing_ts($b->billing_ts);
29                                 $s->last_billing_type($b->billing_type);
30                         }
31                 }
32
33                 $s->total_owed( sprintf('%0.2f', ($to) / 100 ) );
34
35                 my $tp = 0;
36                 my $lp = undef;
37                 for my $p ($x->payments) {
38             #$log->debug( "payment is ".$p->amount." voided = ".$p->voided, DEBUG );
39                         next if ($p->voided eq 't');
40                         $tp += ($p->amount * 100);
41                         $lp ||= $p->payment_ts;
42                         if ($p->payment_ts ge $lp) {
43                                 $lp = $p->payment_ts;
44                                 $s->last_payment_note($p->note);
45                                 $s->last_payment_ts($p->payment_ts);
46                                 $s->last_payment_type($p->payment_type);
47                         }
48                 }
49                 $s->total_paid( sprintf('%0.2f', ($tp) / 100 ) );
50
51                 $s->balance_owed( sprintf('%0.2f', (($to) - ($tp)) / 100) );
52         #$log->debug( "balance of ".$x->id." == ".$s->balance_owed, DEBUG );
53
54                 if (action::circulation->retrieve($x->id)) {
55                     $s->xact_type( 'circulation' );
56                 } elsif (money::grocery->retrieve($x->id)) {
57                     $s->xact_type( 'grocery' );
58                 } elsif (booking::reservation->retrieve($x->id)) {
59                     $s->xact_type( 'reservation' );
60                 }
61
62                 push @mbts, $s;
63         }
64
65         return @mbts;
66 }
67
68 sub search_mbts {
69     my $self = shift;
70     my $client = shift;
71     my $search = shift;
72
73     my @xacts = money::billable_transaction->search_where( $search );
74     $client->respond( $_ ) for (_make_mbts(@xacts));
75
76     return undef;
77 }
78 __PACKAGE__->register_method(
79     method      => 'search_mbts',
80     api_name    => 'open-ils.storage.money.billable_transaction.summary.search',
81     stream      => 1,
82     argc        => 1,
83 );
84
85 sub search_ous {
86     my $self = shift;
87     my $client = shift;
88     my $usr = shift;
89
90     my @xacts = $self->method_lookup( 'open-ils.storage.money.billable_transaction.summary.search' )->run( { usr => $usr, xact_finish => undef } );
91
92     my ($total,$owed,$paid) = (0.0,0.0,0.0);
93     for my $x (@xacts) {
94         $total += $x->total_owed;
95         $owed += $x->balance_owed;
96         $paid += $x->total_paid;
97     }
98
99     my $ous = Fieldmapper::money::open_user_summary->new;
100     $ous->usr( $usr );
101     $ous->total_paid( sprintf('%0.2f', $paid) );
102     $ous->total_owed( sprintf('%0.2f', $total) );
103     $ous->balance_owed( sprintf('%0.2f', $owed) );
104
105     return $ous;
106 }
107 __PACKAGE__->register_method(
108     method      => 'search_ous',
109     api_name    => 'open-ils.storage.money.open_user_summary.search',
110     argc        => 1,
111 );
112
113
114 sub new_collections {
115     my $self = shift;
116     my $client = shift;
117     my $age = shift;
118     my $amount = shift;
119     my @loc = @_;
120
121     my $mct = money::collections_tracker->table;
122     my $mb = money::billing->table;
123     my $circ = action::circulation->table;
124     my $mg = money::grocery->table;
125     my $res = booking::reservation->table;
126     my $descendants = "actor.org_unit_descendants((select id from actor.org_unit where shortname = ?))";
127
128     my $SQL = <<"    SQL";
129
130 select
131         usr,
132         MAX(last_billing) as last_pertinent_billing,
133         SUM(total_billing) - SUM(COALESCE(p.amount,0)) as threshold_amount
134   from  (select
135                 x.id,
136                 x.usr,
137                 MAX(b.billing_ts) as last_billing,
138                 SUM(b.amount) AS total_billing
139           from  action.circulation x
140                 left join money.collections_tracker c ON (c.usr = x.usr AND c.location = ?)
141                 join money.billing b on (b.xact = x.id)
142                 LEFT JOIN actor.usr_setting set ON (set.usr = x.usr and set.name='circ.collections.exempt' and set.value = 'true')
143           where x.xact_finish is null
144                 and c.id is null
145                 and x.circ_lib in (XX)
146                 and b.billing_ts < current_timestamp - ? * '1 day'::interval
147                 and not b.voided
148                 and set.id IS NULL
149           group by 1,2
150
151                   union all
152
153          select
154                 x.id,
155                 x.usr,
156                 MAX(b.billing_ts) as last_billing,
157                 SUM(b.amount) AS total_billing
158           from  money.grocery x
159                 left join money.collections_tracker c ON (c.usr = x.usr AND c.location = ?)
160                 join money.billing b on (b.xact = x.id)
161                 LEFT JOIN actor.usr_setting set ON (set.usr = x.usr and set.name='circ.collections.exempt' and set.value = 'true')
162           where x.xact_finish is null
163                 and c.id is null
164                 and x.billing_location in (XX)
165                 and b.billing_ts < current_timestamp - ? * '1 day'::interval
166                 and not b.voided
167                 and set.id IS NULL
168           group by 1,2
169
170                   union all
171
172          select
173                 x.id,
174                 x.usr,
175                 MAX(b.billing_ts) as last_billing,
176                 SUM(b.amount) AS total_billing
177           from  booking.reservation x
178                 left join money.collections_tracker c ON (c.usr = x.usr AND c.location = ?)
179                 join money.billing b on (b.xact = x.id)
180                 LEFT JOIN actor.usr_setting set ON (set.usr = x.usr and set.name='circ.collections.exempt' and set.value = 'true')
181           where x.xact_finish is null
182                 and c.id is null
183                 and x.pickup_lib in (XX)
184                 and b.billing_ts < current_timestamp - ? * '1 day'::interval
185                 and not b.voided
186                 and set.id IS NULL
187           group by 1,2
188         ) full_list
189         left join money.payment p on (full_list.id = p.xact)
190   group by 1
191   having SUM(total_billing) - SUM(COALESCE(p.amount,0)) > ?
192 ;
193     SQL
194
195     my @l_ids;
196     for my $l (@loc) {
197         my ($org) = actor::org_unit->search( shortname => uc($l) );
198         next unless $org;
199
200         my $o_list = actor::org_unit->db_Main->selectcol_arrayref( "SELECT id FROM actor.org_unit_descendants(?);", {}, $org->id );
201         next unless (@$o_list);
202
203         my $o_txt = join ',' => @$o_list;
204
205         (my $real_sql = $SQL) =~ s/XX/$o_txt/gsm;
206
207         my $sth = money::collections_tracker->db_Main->prepare($real_sql);
208         $sth->execute( $org->id, $age, $org->id, $age, $org->id, $age, $amount );
209
210         while (my $row = $sth->fetchrow_hashref) {
211             #$row->{usr} = actor::user->retrieve($row->{usr})->to_fieldmapper;
212             $client->respond( $row );
213         }
214     }
215     return undef;
216 }
217 __PACKAGE__->register_method(
218     method      => 'new_collections',
219     api_name    => 'open-ils.storage.money.collections.users_of_interest',
220     stream      => 1,
221     argc        => 3,
222 );
223
224 sub users_owing_money {
225     my $self = shift;
226     my $client = shift;
227     my $start = shift;
228     my $end = shift;
229     my $amount = shift;
230     my @loc = @_;
231
232     my $mct = money::collections_tracker->table;
233     my $mb = money::billing->table;
234     my $circ = action::circulation->table;
235     my $mg = money::grocery->table;
236     my $descendants = "actor.org_unit_descendants((select id from actor.org_unit where shortname = ?))";
237
238     my $SQL = <<"    SQL";
239
240 select
241         usr,
242         SUM(total_billing) - SUM(COALESCE(p.amount,0)) as threshold_amount
243   from  (select
244                 x.id,
245                 x.usr,
246                 SUM(b.amount) AS total_billing
247           from  action.circulation x
248                 join money.billing b on (b.xact = x.id)
249           where x.xact_finish is null
250                 and x.circ_lib in (XX)
251                 and b.billing_ts between ? and ?
252                 and not b.voided
253           group by 1,2
254
255                   union all
256
257          select
258                 x.id,
259                 x.usr,
260                 SUM(b.amount) AS total_billing
261           from  money.grocery x
262                 join money.billing b on (b.xact = x.id)
263           where x.xact_finish is null
264                 and x.billing_location in (XX)
265                 and b.billing_ts between ? and ?
266                 and not b.voided
267           group by 1,2
268
269                   union all
270
271          select
272                 x.id,
273                 x.usr,
274                 SUM(b.amount) AS total_billing
275           from  booking.reservation x
276                 join money.billing b on (b.xact = x.id)
277           where x.xact_finish is null
278                 and x.pickup_lib in (XX)
279                 and b.billing_ts between ? and ?
280                 and not b.voided
281           group by 1,2
282         ) full_list
283         left join money.payment p on (full_list.id = p.xact)
284   group by 1
285   having SUM(total_billing) - SUM(COALESCE(p.amount,0)) > ?
286 ;
287     SQL
288
289     my @l_ids;
290     for my $l (@loc) {
291         my ($org) = actor::org_unit->search( shortname => uc($l) );
292         next unless $org;
293
294         my $o_list = actor::org_unit->db_Main->selectcol_arrayref( "SELECT id FROM actor.org_unit_descendants(?);", {}, $org->id );
295         next unless (@$o_list);
296
297         my $o_txt = join ',' => @$o_list;
298
299         (my $real_sql = $SQL) =~ s/XX/$o_txt/gsm;
300
301         my $sth = money::collections_tracker->db_Main->prepare($real_sql);
302         $sth->execute( $start, $end, $start, $end, $amount );
303
304         while (my $row = $sth->fetchrow_hashref) {
305             #$row->{usr} = actor::user->retrieve($row->{usr})->to_fieldmapper;
306             $client->respond( $row );
307         }
308     }
309     return undef;
310 }
311 __PACKAGE__->register_method(
312     method      => 'users_owing_money',
313     api_name    => 'open-ils.storage.money.collections.users_owing_money',
314     stream      => 1,
315     argc        => 4,
316 );
317
318 sub active_in_collections {
319     my $self = shift;
320     my $client = shift;
321     my $startdate = shift;
322     my $enddate = shift;
323     my @loc = @_;
324
325     my $mct = money::collections_tracker->table;
326     my $mb = money::billing->table;
327     my $circ = action::circulation->table;
328     my $mg = money::grocery->table;
329
330     my $SQL = <<"    SQL";
331 SELECT  usr,
332         MAX(last_pertinent_billing) AS last_pertinent_billing,
333         MAX(last_pertinent_payment) AS last_pertinent_payment
334   FROM  (
335                 SELECT  lt.usr,
336                         NULL::TIMESTAMPTZ AS last_pertinent_billing,
337                         NULL::TIMESTAMPTZ AS last_pertinent_payment
338                   FROM  booking.reservation lt
339                         JOIN money.collections_tracker cl ON (lt.usr = cl.usr)
340                         JOIN money.billing bl ON (lt.id = bl.xact)
341                   WHERE cl.location = ?
342                         AND lt.pickup_lib IN (XX)
343                         AND bl.void_time BETWEEN ? AND ?
344                   GROUP BY 1
345
346                                 UNION ALL
347                 SELECT  lt.usr,
348                         MAX(bl.billing_ts) AS last_pertinent_billing,
349                         NULL::TIMESTAMPTZ AS last_pertinent_payment
350                   FROM  booking.reservation lt
351                         JOIN money.collections_tracker cl ON (lt.usr = cl.usr)
352                         JOIN money.billing bl ON (lt.id = bl.xact)
353                   WHERE cl.location = ?
354                         AND lt.pickup_lib IN (XX)
355                         AND bl.billing_ts BETWEEN ? AND ?
356                   GROUP BY 1
357
358                                 UNION ALL
359                 SELECT  lt.usr,
360                         NULL::TIMESTAMPTZ AS last_pertinent_billing,
361                         MAX(pm.payment_ts) AS last_pertinent_payment
362                   FROM  booking.reservation lt
363                         JOIN money.collections_tracker cl ON (lt.usr = cl.usr)
364                         JOIN money.payment pm ON (lt.id = pm.xact)
365                   WHERE cl.location = ?
366                         AND lt.pickup_lib IN (XX)
367                         AND pm.payment_ts BETWEEN ? AND ?
368                   GROUP BY 1
369
370                                 UNION ALL
371                  SELECT  lt.usr,
372                         NULL::TIMESTAMPTZ AS last_pertinent_billing,
373                         NULL::TIMESTAMPTZ AS last_pertinent_payment
374                   FROM  money.grocery lt
375                         JOIN money.collections_tracker cl ON (lt.usr = cl.usr)
376                         JOIN money.billing bl ON (lt.id = bl.xact)
377                   WHERE cl.location = ?
378                         AND lt.billing_location IN (XX)
379                         AND bl.void_time BETWEEN ? AND ?
380                   GROUP BY 1
381
382                                 UNION ALL
383                 SELECT  lt.usr,
384                         MAX(bl.billing_ts) AS last_pertinent_billing,
385                         NULL::TIMESTAMPTZ AS last_pertinent_payment
386                   FROM  money.grocery lt
387                         JOIN money.collections_tracker cl ON (lt.usr = cl.usr)
388                         JOIN money.billing bl ON (lt.id = bl.xact)
389                   WHERE cl.location = ?
390                         AND lt.billing_location IN (XX)
391                         AND bl.billing_ts BETWEEN ? AND ?
392                   GROUP BY 1
393
394                                 UNION ALL
395                 SELECT  lt.usr,
396                         NULL::TIMESTAMPTZ AS last_pertinent_billing,
397                         MAX(pm.payment_ts) AS last_pertinent_payment
398                   FROM  money.grocery lt
399                         JOIN money.collections_tracker cl ON (lt.usr = cl.usr)
400                         JOIN money.payment pm ON (lt.id = pm.xact)
401                   WHERE cl.location = ?
402                         AND lt.billing_location IN (XX)
403                         AND pm.payment_ts BETWEEN ? AND ?
404                   GROUP BY 1
405
406                                 UNION ALL
407                 SELECT  lt.usr,
408                         NULL::TIMESTAMPTZ AS last_pertinent_billing,
409                         NULL::TIMESTAMPTZ AS last_pertinent_payment
410                   FROM  action.circulation lt
411                         JOIN money.collections_tracker cl ON (lt.usr = cl.usr)
412                   WHERE cl.location = ?
413                         AND lt.circ_lib IN (XX)
414                         AND lt.checkin_time BETWEEN ? AND ?
415                   GROUP BY 1
416
417                                 UNION ALL
418                 SELECT  lt.usr,
419                         NULL::TIMESTAMPTZ AS last_pertinent_billing,
420                         MAX(pm.payment_ts) AS last_pertinent_payment
421                   FROM  action.circulation lt
422                         JOIN money.collections_tracker cl ON (lt.usr = cl.usr)
423                         JOIN money.payment pm ON (lt.id = pm.xact)
424                   WHERE cl.location = ?
425                         AND lt.circ_lib IN (XX)
426                         AND pm.payment_ts BETWEEN ? AND ?
427                   GROUP BY 1
428
429                                 UNION ALL
430                 SELECT  lt.usr,
431                         NULL::TIMESTAMPTZ AS last_pertinent_billing,
432                         NULL::TIMESTAMPTZ AS last_pertinent_payment
433                   FROM  action.circulation lt
434                         JOIN money.collections_tracker cl ON (lt.usr = cl.usr)
435                         JOIN money.billing bl ON (lt.id = bl.xact)
436                   WHERE cl.location = ?
437                         AND lt.circ_lib IN (XX)
438                         AND bl.void_time BETWEEN ? AND ?
439                   GROUP BY 1
440
441                                 UNION ALL
442                 SELECT  lt.usr,
443                         MAX(bl.billing_ts) AS last_pertinent_billing,
444                         NULL::TIMESTAMPTZ AS last_pertinent_payment
445                   FROM  action.circulation lt
446                         JOIN money.collections_tracker cl ON (lt.usr = cl.usr)
447                         JOIN money.billing bl ON (lt.id = bl.xact)
448                   WHERE cl.location = ?
449                         AND lt.circ_lib IN (XX)
450                         AND bl.billing_ts BETWEEN ? AND ?
451                   GROUP BY 1
452         ) foo
453   GROUP BY 1
454 ;
455     SQL
456
457     my @l_ids;
458     for my $l (@loc) {
459         my ($org) = actor::org_unit->search( shortname => uc($l) );
460         next unless $org;
461
462         my $o_list = actor::org_unit->db_Main->selectcol_arrayref( "SELECT id FROM actor.org_unit_descendants(?);", {}, $org->id );
463         next unless (@$o_list);
464
465         my $o_txt = join ',' => @$o_list;
466
467         (my $real_sql = $SQL) =~ s/XX/$o_txt/gsm;
468
469         my $sth = money::collections_tracker->db_Main->prepare($real_sql);
470         $sth->execute(
471             # reservation queries
472             $org->id, $startdate, $enddate,
473             $org->id, $startdate, $enddate,
474             $org->id, $startdate, $enddate,
475
476             # grocery queries
477             $org->id, $startdate, $enddate,
478             $org->id, $startdate, $enddate,
479             $org->id, $startdate, $enddate,
480
481             # circ queries
482             $org->id, $startdate, $enddate,
483             $org->id, $startdate, $enddate,
484             $org->id, $startdate, $enddate,
485             $org->id, $startdate, $enddate
486         );
487
488         while (my $row = $sth->fetchrow_hashref) {
489             $row->{usr} = actor::user->retrieve($row->{usr})->to_fieldmapper;
490             $client->respond( $row );
491         }
492     }
493     return undef;
494 }
495 __PACKAGE__->register_method(
496     method      => 'active_in_collections',
497     api_name    => 'open-ils.storage.money.collections.users_with_activity',
498     stream      => 1,
499     argc        => 3,
500 );
501
502 sub ou_desk_payments {
503     my $self = shift;
504     my $client = shift;
505     my $lib = shift;
506     my $startdate = shift;
507     my $enddate = shift;
508
509     return undef unless ($startdate =~ /^\d{4}-\d{2}-\d{2}$/o);
510     return undef unless ($enddate =~ /^\d{4}-\d{2}-\d{2}$/o);
511     return undef unless ($lib =~ /^\d+$/o);
512
513     my $sql = <<"    SQL";
514
515     SELECT  ws.id as workstation,
516         SUM( CASE WHEN p.payment_type = 'cash_payment' THEN p.amount ELSE 0.0 END ) as cash_payment,
517         SUM( CASE WHEN p.payment_type = 'check_payment' THEN p.amount ELSE 0.0 END ) as check_payment,
518         SUM( CASE WHEN p.payment_type = 'credit_card_payment' THEN p.amount ELSE 0.0 END ) as credit_card_payment
519       FROM  money.desk_payment_view p
520         JOIN actor.workstation ws ON (ws.id = p.cash_drawer)
521       WHERE p.payment_ts >= '$startdate'
522         AND p.payment_ts < '$enddate'::TIMESTAMPTZ + INTERVAL '1 day'
523         AND p.voided IS FALSE
524         AND ws.owning_lib = $lib
525      GROUP BY 1
526      ORDER BY 1;
527
528     SQL
529
530     my $rows = money::payment->db_Main->selectall_arrayref( $sql );
531
532     for my $r (@$rows) {
533         my $x = new Fieldmapper::money::workstation_payment_summary;
534         $x->workstation( actor::workstation->retrieve($$r[0])->to_fieldmapper );
535         $x->cash_payment($$r[1]);
536         $x->check_payment($$r[2]);
537         $x->credit_card_payment($$r[3]);
538
539         $client->respond($x);
540     }
541
542     return undef;
543 }
544 __PACKAGE__->register_method(
545     method      => 'ou_desk_payments',
546     api_name    => 'open-ils.storage.money.org_unit.desk_payments',
547     stream      => 1,
548     argc        => 3,
549 );
550
551 sub ou_user_payments {
552     my $self = shift;
553     my $client = shift;
554     my $lib = shift;
555     my $startdate = shift;
556     my $enddate = shift;
557
558     return undef unless ($startdate =~ /^\d{4}-\d{2}-\d{2}$/o);
559     return undef unless ($enddate =~ /^\d{4}-\d{2}-\d{2}$/o);
560     return undef unless ($lib =~ /^\d+$/o);
561
562     my $sql = <<"    SQL";
563
564         SELECT  au.id as usr,
565         SUM( CASE WHEN p.payment_type = 'forgive_payment' THEN p.amount ELSE 0.0 END ) as forgive_payment,
566         SUM( CASE WHEN p.payment_type = 'work_payment' THEN p.amount ELSE 0.0 END ) as work_payment,
567         SUM( CASE WHEN p.payment_type = 'credit_payment' THEN p.amount ELSE 0.0 END ) as credit_payment,
568         SUM( CASE WHEN p.payment_type = 'goods_payment' THEN p.amount ELSE 0.0 END ) as goods_payment
569           FROM  money.bnm_payment_view p
570                 JOIN actor.usr au ON (au.id = p.accepting_usr)
571           WHERE p.payment_ts >= '$startdate'
572                 AND p.payment_ts < '$enddate'::TIMESTAMPTZ + INTERVAL '1 day'
573                 AND p.voided IS FALSE
574                 AND au.home_ou = $lib
575         AND p.payment_type IN ('credit_payment','forgive_payment','work_payment','goods_payment')
576          GROUP BY 1
577          ORDER BY 1;
578
579     SQL
580
581     my $rows = money::payment->db_Main->selectall_arrayref( $sql );
582
583     for my $r (@$rows) {
584         my $x = new Fieldmapper::money::user_payment_summary;
585         $x->usr( actor::user->retrieve($$r[0])->to_fieldmapper );
586         $x->forgive_payment($$r[1]);
587         $x->work_payment($$r[2]);
588         $x->credit_payment($$r[3]);
589         $x->goods_payment($$r[4]);
590
591         $client->respond($x);
592     }
593
594     return undef;
595 }
596 __PACKAGE__->register_method(
597     method      => 'ou_user_payments',
598     api_name    => 'open-ils.storage.money.org_unit.user_payments',
599     stream      => 1,
600     argc        => 3,
601 );
602
603 sub mark_unrecovered {
604     my $self = shift;
605     my $xact = shift;
606
607     my $x = money::billable_xact->retrieve($xact);
608     $x->unrecovered( 't' );
609     return $x->update;
610 }
611 __PACKAGE__->register_method(
612     method      => 'mark_unrecovered',
613     api_name    => 'open-ils.storage.money.billable_xact.mark_unrecovered',
614     argc        => 1,
615 );
616
617
618 1;