From: miker Date: Thu, 3 Dec 2009 21:29:05 +0000 (+0000) Subject: integrate the new booking.reservation billable transaction table with all the parts... X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=aea92deae8ee537cf39a316512df03b8eab7a084;p=evergreen%2Fbjwebb.git integrate the new booking.reservation billable transaction table with all the parts that use grocery transactions, including in the materialized summary view git-svn-id: svn://svn.open-ils.org/ILS/trunk@15071 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Circ.pm b/Open-ILS/src/perlmods/OpenILS/Application/Circ.pm index cdd595156..1fecadd65 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Circ.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Circ.pm @@ -1379,7 +1379,8 @@ sub user_payments_list { ], mbt => [ 'circulation', - 'grocery' + 'grocery', + 'reservation' ] } } diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Circ/Money.pm b/Open-ILS/src/perlmods/OpenILS/Application/Circ/Money.pm index 0102f4ee7..30e2a82a4 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Circ/Money.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Circ/Money.pm @@ -433,6 +433,20 @@ sub create_grocery_bill { __PACKAGE__->register_method( + method => 'fetch_reservation', + api_name => 'open-ils.circ.booking.reservation.retrieve' +); +sub fetch_grocery { + my( $self, $conn, $auth, $id ) = @_; + my $e = new_editor(authtoken=>$auth); + return $e->event unless $e->checkauth; + return $e->event unless $e->allowed('VIEW_TRANSACTION'); # eh.. basically the same permission + my $g = $e->retrieve_booking_reservation($id) + or return $e->event; + return $g; +} + +__PACKAGE__->register_method( method => 'fetch_grocery', api_name => 'open-ils.circ.money.grocery.retrieve' ); diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Collections.pm b/Open-ILS/src/perlmods/OpenILS/Application/Collections.pm index 050a9d157..8b72ae9a3 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Collections.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Collections.pm @@ -621,7 +621,9 @@ sub transaction_details { circulations => fetch_circ_xacts($e, $uid, $org, $start_date, $end_date), grocery => - fetch_grocery_xacts($e, $uid, $org, $start_date, $end_date) + fetch_grocery_xacts($e, $uid, $org, $start_date, $end_date), + reservations => + fetch_reservation_xacts($e, $uid, $org, $start_date, $end_date) }; # for each transaction, flesh the workstatoin on any attached payment @@ -629,6 +631,7 @@ sub transaction_details { # not just a generic payment object for my $xact ( @{$blob->{transactions}->{circulations}}, + @{$blob->{transactions}->{reservations}}, @{$blob->{transactions}->{grocery}} ) { my $ps; @@ -778,6 +781,53 @@ sub fetch_grocery_xacts { return \@data; } +sub fetch_reservation_xacts { + my $e = shift; + my $uid = shift; + my $org = shift; + my $start_date = shift; + my $end_date = shift; + + my @xacts; + $U->walk_org_tree( $org, + sub { + my $n = shift; + $logger->debug("collect: searching for open grocery xacts at " . $n->shortname); + push( @xacts, + @{ + $e->search_booking_reservation( + { + usr => $uid, + pickup_lib => $n->id, + }, + {idlist => 1} + ) + } + ); + } + ); + + my @data; + my $active_ids = fetch_active($e, \@xacts, $start_date, $end_date); + + for my $id (@$active_ids) { + push( @data, + $e->retrieve_booking_reservation( + [ + $id, + { + flesh => 1, + flesh_fields => { + bresv => [ "billings", "payments", "pickup_lib" ] } + } + ] + ) + ); + } + + return \@data; +} + # -------------------------------------------------------------- diff --git a/Open-ILS/src/perlmods/OpenILS/Application/CreditCard.pm b/Open-ILS/src/perlmods/OpenILS/Application/CreditCard.pm index 55e3b5c13..843540e5b 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/CreditCard.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/CreditCard.pm @@ -359,9 +359,12 @@ sub retrieve_payable_balance { my $circ = $e->retrieve_action_circulation($xact->id) or return $e->event; next unless grep { $_ == $circ->circ_lib } @credit_orgs; - } else { + } elsif ($xact->xact_type eq 'grocery') { my $bill = $e->retrieve_money_grocery($xact->id) or return $e->event; next unless grep { $_ == $bill->billing_location } @credit_orgs; + } elsif ($xact->xact_type eq 'reservation') { + my $bill = $e->retrieve_booking_reservation($xact->id) or return $e->event; + next unless grep { $_ == $bill->pickup_lib } @credit_orgs; } $sum += $xact->balance_owed(); } diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI.pm b/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI.pm index 1388d816c..c2b0f5684 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI.pm @@ -5,6 +5,7 @@ use Class::DBI::AbstractSearch; use OpenILS::Application::Storage::CDBI::actor; use OpenILS::Application::Storage::CDBI::action; +use OpenILS::Application::Storage::CDBI::booking; use OpenILS::Application::Storage::CDBI::asset; use OpenILS::Application::Storage::CDBI::authority; use OpenILS::Application::Storage::CDBI::biblio; @@ -555,6 +556,9 @@ sub modify_from_fieldmapper { action::circulation->has_a( usr => 'actor::user' ); actor::user->has_many( circulations => 'action::circulation' => 'usr' ); + + booking::reservation->has_a( usr => 'actor::user' ); + actor::user->has_many( reservations => 'booking::reservation' => 'usr' ); action::circulation->has_a( circ_staff => 'actor::user' ); actor::user->has_many( performed_circulations => 'action::circulation' => 'circ_staff' ); @@ -565,6 +569,8 @@ sub modify_from_fieldmapper { action::circulation->has_a( target_copy => 'asset::copy' ); asset::copy->has_many( circulations => 'action::circulation' => 'target_copy' ); + booking::reservation->has_a( pickup_lib => 'actor::org_unit' ); + action::circulation->has_a( circ_lib => 'actor::org_unit' ); actor::org_unit->has_many( circulations => 'action::circulation' => 'circ_lib' ); @@ -621,9 +627,11 @@ sub modify_from_fieldmapper { action::circulation->has_many( billings => 'money::billing' => 'xact' ); action::circulation->has_many( payments => 'money::payment' => 'xact' ); #action::circulation->might_have( billable_transaction => 'money::billable_transaction' ); - #action::open_circulation->might_have( circulation => 'action::circulation' ); + booking::reservation->has_many( billings => 'money::billing' => 'xact' ); + booking::reservation->has_many( payments => 'money::payment' => 'xact' ); + action::in_house_use->has_a( org_unit => 'actor::org_unit' ); action::in_house_use->has_a( staff => 'actor::user' ); action::in_house_use->has_a( item => 'asset::copy' ); diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/booking.pm b/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/booking.pm new file mode 100644 index 000000000..7a7bb362b --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/booking.pm @@ -0,0 +1,23 @@ +package OpenILS::Application::Storage::CDBI::booking; +our $VERSION = 1; + +#------------------------------------------------------------------------------- +package booking; +use base qw/OpenILS::Application::Storage::CDBI/; +#------------------------------------------------------------------------------- + +package booking::reservation; +use base qw/booking/; +__PACKAGE__->table('booking_reservation'); +__PACKAGE__->columns(Primary => 'id'); +__PACKAGE__->columns(Essential => qw/xact_start usr current_copy circ_lib + fine_amount max_fine fine_interval xact_finish + capture_staff pickup_lib request_time start_time end_time + capture_time cancel_time pickup_time return_time + booking_interval target_resource_type target_resource + current_resource request_lib/); + +#------------------------------------------------------------------------------- + +1; + diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/dbi.pm b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/dbi.pm index 0620bd7fa..9939f5a42 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/dbi.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/dbi.pm @@ -161,6 +161,12 @@ action::circulation->sequence( 'money.billable_xact_id_seq' ); #--------------------------------------------------------------------- + package booking::reservation; + + booking::reservation->table( 'booking.reservation' ); + booking::reservation->sequence( 'money.billable_xact_id_seq' ); + + #--------------------------------------------------------------------- package action::non_cat_in_house_use; action::non_cat_in_house_use->table( 'action.non_cat_in_house_use' ); diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm index 82eb65e32..6785a7566 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm @@ -114,7 +114,22 @@ sub overdue_circs { my $sth = action::circulation->db_Main->prepare_cached($sql); $sth->execute($upper_interval); - return ( map { action::circulation->construct($_) } $sth->fetchall_hash ); + my @circs = map { action::circulation->construct($_) } $sth->fetchall_hash; + + $c_t = booking::reservation->table; + $sql = <<" SQL"; + SELECT * + FROM $c_t + WHERE return_time IS NULL + AND end_time < ( CURRENT_TIMESTAMP $grace) + AND fine_interval IS NOT NULL + AND cancel_time IS NULL + SQL + + my $sth = action::circulation->db_Main->prepare_cached($sql); + $sth->execute($upper_interval); + + my @circs = map { booking::reservation->construct($_) } $sth->fetchall_hash; } @@ -638,7 +653,9 @@ sub generate_fines { my @circs; if ($circ) { - push @circs, action::circulation->search_where( { id => $circ, stop_fines => undef } ); + push @circs, + action::circulation->search_where( { id => $circ, stop_fines => undef } ), + booking::reservation->search_where( { id => $circ, return_time => undef, cancel_time => undef } ); } else { push @circs, overdue_circs($grace); } @@ -647,7 +664,22 @@ sub generate_fines { my $penalty = OpenSRF::AppSession->create('open-ils.penalty'); for my $c (@circs) { + + my $ctype = ref($c); + $ctype =~ s/^.*([^:]+)$/$1/o; + my $due_date_method = 'due_date'; + my $target_copy_method = 'target_copy'; + my $circ_lib_method = 'circ_lib'; + my $recurring_fine_method = 'recurring_fine'; + if ($ctype eq 'reservation') { + $due_date_method = 'end_time'; + $target_copy_method = 'current_resource'; + $circ_lib_method = 'pickup_lib'; + $recurring_fine_method = 'fine_amount'; + next unless ($c->fine_interval); + } + try { if ($self->method_lookup('open-ils.storage.transaction.current')->run) { $log->debug("Cleaning up after previous transaction\n"); @@ -657,7 +689,7 @@ sub generate_fines { $log->info("Processing circ ".$c->id."...\n"); - my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->due_date ) ); + my $due_dt = $parser->parse_datetime( clense_ISO8601( $c->$due_date_method ) ); my $due = $due_dt->epoch; my $now = time; @@ -677,15 +709,15 @@ sub generate_fines { } $client->respond( - "ARG! Overdue circulation ".$c->id. - " for item ".$c->target_copy. + "ARG! Overdue $ctype ".$c->id. + " for item ".$c->$target_copy_method. " (user ".$c->usr.").\n". "\tItem was due on or before: ".localtime($due)."\n"); my @fines = money::billing->search_where( { xact => $c->id, btype => 1, - billing_ts => { '>' => $c->due_date } }, + billing_ts => { '>' => $c->$due_date_method } }, { order_by => 'billing_ts DESC'} ); @@ -707,7 +739,7 @@ sub generate_fines { $last_fine = $due; if (0) { - if (my $h = $hoo{$c->circ_lib}) { + if (my $h = $hoo{$c->$circ_lib_method}) { $log->info( "Circ lib has an hours-of-operation entry" ); # find the day after the due date... @@ -755,17 +787,17 @@ sub generate_fines { $client->respond( "\t$pending_fine_count pending fine(s)\n" ); - my $recurring_fine = int($c->recurring_fine * 100); + my $recurring_fine = int($c->$recurring_fine_method * 100); my $max_fine = int($c->max_fine * 100); my ($latest_billing_ts, $latest_amount) = ('',0); for (my $bill = 1; $bill <= $pending_fine_count; $bill++) { if ($current_fine_total >= $max_fine) { - $c->update({stop_fines => 'MAXFINES', stop_fines_time => 'now'}); + $c->update({stop_fines => 'MAXFINES', stop_fines_time => 'now'}) if ($ctype eq 'circulation'); $client->respond( "\tMaximum fine level of ".$c->max_fine. - " reached for this circulation.\n". + " reached for this $ctype.\n". "\tNo more fines will be generated.\n" ); last; } @@ -776,7 +808,7 @@ sub generate_fines { my $dow_open = "dow_${dow}_open"; my $dow_close = "dow_${dow}_close"; - if (my $h = $hoo{$c->circ_lib}) { + if (my $h = $hoo{$c->$circ_lib_method}) { next if ( $h->$dow_open eq '00:00:00' and $h->$dow_close eq '00:00:00'); } @@ -784,7 +816,7 @@ sub generate_fines { my @cl = actor::org_unit::closed_date->search_where( { close_start => { '<=' => $timestamptz }, close_end => { '>=' => $timestamptz }, - org_unit => $c->circ_lib } + org_unit => $c->$circ_lib_method } ); next if (@cl); @@ -813,7 +845,7 @@ sub generate_fines { # Caluclate penalties inline OpenILS::Utils::Penalty->calculate_penalties( - undef, $c->usr->to_fieldmapper->id.'', $c->circ_lib->to_fieldmapper->id.''); + undef, $c->usr->to_fieldmapper->id.'', $c->$circ_lib_method->to_fieldmapper->id.''); } else { @@ -824,7 +856,7 @@ sub generate_fines { $penalty->request( 'open-ils.penalty.patron_penalty.calculate', { patronid => ''.$c->usr, - context_org => ''.$c->circ_lib, + context_org => ''.$c->$circ_lib_method, update => 1, background => 1, } @@ -833,8 +865,8 @@ sub generate_fines { } catch Error with { my $e = shift; - $client->respond( "Error processing overdue circulation [".$c->id."]:\n\n$e\n" ); - $log->error("Error processing overdue circulation [".$c->id."]:\n$e\n"); + $client->respond( "Error processing overdue $ctype [".$c->id."]:\n\n$e\n" ); + $log->error("Error processing overdue $ctype [".$c->id."]:\n$e\n"); $self->method_lookup('open-ils.storage.transaction.rollback')->run; throw $e if ($e =~ /IS NOT CONNECTED TO THE NETWORK/o); }; diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/money.pm b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/money.pm index b049a0aae..e1a2f0ae3 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/money.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/money.pm @@ -51,8 +51,13 @@ sub _make_mbts { $s->balance_owed( sprintf('%0.2f', (($to) - ($tp)) / 100) ); #$log->debug( "balance of ".$x->id." == ".$s->balance_owed, DEBUG ); - $s->xact_type( 'grocery' ) if (money::grocery->retrieve($x->id)); - $s->xact_type( 'circulation' ) if (action::circulation->retrieve($x->id)); + if (action::circulation->retrieve($x->id)) { + $s->xact_type( 'circulation' ); + } elsif (money::grocery->retrieve($x->id)) { + $s->xact_type( 'grocery' ); + } elsif (booking::reservation->retrieve($x->id)) { + $s->xact_type( 'reservation' ); + } push @mbts, $s; } @@ -117,6 +122,7 @@ sub new_collections { my $mb = money::billing->table; my $circ = action::circulation->table; my $mg = money::grocery->table; + my $res = booking::reservation->table; my $descendants = "actor.org_unit_descendants((select id from actor.org_unit where shortname = ?))"; my $SQL = <<" SQL"; @@ -156,6 +162,23 @@ select and b.billing_ts < current_timestamp - ? * '1 day'::interval and not b.voided group by 1,2 + + union all + + select + x.id, + x.usr, + MAX(b.billing_ts) as last_billing, + SUM(b.amount) AS total_billing + from booking.reservation x + left join money.collections_tracker c ON (c.usr = x.usr AND c.location = ?) + join money.billing b on (b.xact = x.id) + where x.xact_finish is null + and c.id is null + and x.pickup_lib in (XX) + and b.billing_ts < current_timestamp - ? * '1 day'::interval + and not b.voided + group by 1,2 ) full_list left join money.payment p on (full_list.id = p.xact) group by 1 @@ -236,6 +259,20 @@ select and b.billing_ts between ? and ? and not b.voided group by 1,2 + + union all + + select + x.id, + x.usr, + SUM(b.amount) AS total_billing + from booking.reservation x + join money.billing b on (b.xact = x.id) + where x.xact_finish is null + and x.pickup_lib in (XX) + and b.billing_ts between ? and ? + and not b.voided + group by 1,2 ) full_list left join money.payment p on (full_list.id = p.xact) group by 1 @@ -292,6 +329,42 @@ SELECT usr, SELECT lt.usr, NULL::TIMESTAMPTZ AS last_pertinent_billing, NULL::TIMESTAMPTZ AS last_pertinent_payment + FROM booking.reservation lt + JOIN money.collections_tracker cl ON (lt.usr = cl.usr) + JOIN money.billing bl ON (lt.id = bl.xact) + WHERE cl.location = ? + AND lt.pickup_lib IN (XX) + AND bl.void_time BETWEEN ? AND ? + GROUP BY 1 + + UNION ALL + SELECT lt.usr, + MAX(bl.billing_ts) AS last_pertinent_billing, + NULL::TIMESTAMPTZ AS last_pertinent_payment + FROM booking.reservation lt + JOIN money.collections_tracker cl ON (lt.usr = cl.usr) + JOIN money.billing bl ON (lt.id = bl.xact) + WHERE cl.location = ? + AND lt.pickup_lib IN (XX) + AND bl.billing_ts BETWEEN ? AND ? + GROUP BY 1 + + UNION ALL + SELECT lt.usr, + NULL::TIMESTAMPTZ AS last_pertinent_billing, + MAX(pm.payment_ts) AS last_pertinent_payment + FROM booking.reservation lt + JOIN money.collections_tracker cl ON (lt.usr = cl.usr) + JOIN money.payment pm ON (lt.id = pm.xact) + WHERE cl.location = ? + AND lt.pickup_lib IN (XX) + AND pm.payment_ts BETWEEN ? AND ? + GROUP BY 1 + + UNION ALL + SELECT lt.usr, + NULL::TIMESTAMPTZ AS last_pertinent_billing, + NULL::TIMESTAMPTZ AS last_pertinent_payment FROM money.grocery lt JOIN money.collections_tracker cl ON (lt.usr = cl.usr) JOIN money.billing bl ON (lt.id = bl.xact) @@ -389,9 +462,17 @@ SELECT usr, my $sth = money::collections_tracker->db_Main->prepare($real_sql); $sth->execute( + # reservation queries $org->id, $startdate, $enddate, $org->id, $startdate, $enddate, $org->id, $startdate, $enddate, + + # grocery queries + $org->id, $startdate, $enddate, + $org->id, $startdate, $enddate, + $org->id, $startdate, $enddate, + + # circ queries $org->id, $startdate, $enddate, $org->id, $startdate, $enddate, $org->id, $startdate, $enddate, diff --git a/Open-ILS/src/perlmods/OpenILS/WWW/BadDebt.pm b/Open-ILS/src/perlmods/OpenILS/WWW/BadDebt.pm index a5015eeed..cb7c322d6 100644 --- a/Open-ILS/src/perlmods/OpenILS/WWW/BadDebt.pm +++ b/Open-ILS/src/perlmods/OpenILS/WWW/BadDebt.pm @@ -97,9 +97,17 @@ sub handler { my $s = $cstore->request('open-ils.cstore.direct.money.billable_xact_summary.retrieve' => $xact)->gather(1); my $u = $cstore->request('open-ils.cstore.direct.actor.usr.retrieve' => $s->usr)->gather(1); my $c = $cstore->request('open-ils.cstore.direct.actor.card.retrieve' => $u->card)->gather(1); - my $w = $s->xact_type eq 'circulation' ? - $cstore->request('open-ils.cstore.direct.action.circulation.retrieve' => $xact)->gather(1)->circ_lib : - $cstore->request('open-ils.cstore.direct.money.grocery.retrieve' => $xact)->gather(1)->billing_location; + my $w; + + if ($s->xact_type eq 'circulation') { + $w = $cstore->request('open-ils.cstore.direct.action.circulation.retrieve' => $xact)->gather(1)->circ_lib : + } elsif ($s->xact_type eq 'grocery') { + $w = $cstore->request('open-ils.cstore.direct.money.grocery.retrieve' => $xact)->gather(1)->billing_location; + } elsif ($s->xact_type eq 'reservation') { + $w = $cstore->request('open-ils.cstore.direct.booking.reservation.retrieve' => $xact)->gather(1)->pickup_lib; + } else { + die; + } my $failures = $actor->request('open-ils.actor.user.perm.check', $auth_ses, $user->id, $w, ['MARK_BAD_DEBT'])->gather(1); diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql index 9975722df..316638785 100644 --- a/Open-ILS/src/sql/Pg/002.schema.config.sql +++ b/Open-ILS/src/sql/Pg/002.schema.config.sql @@ -51,7 +51,7 @@ CREATE TABLE config.upgrade_log ( install_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() ); -INSERT INTO config.upgrade_log (version) VALUES ('0104'); -- Scott McKellar +INSERT INTO config.upgrade_log (version) VALUES ('0105'); -- miker CREATE TABLE config.bib_source ( id SERIAL PRIMARY KEY, diff --git a/Open-ILS/src/sql/Pg/095.schema.booking.sql b/Open-ILS/src/sql/Pg/095.schema.booking.sql index 79dbd767f..7218da4e1 100644 --- a/Open-ILS/src/sql/Pg/095.schema.booking.sql +++ b/Open-ILS/src/sql/Pg/095.schema.booking.sql @@ -137,6 +137,11 @@ ALTER TABLE booking.reservation FOREIGN KEY (usr) REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED; +CREATE TRIGGER mat_summary_create_tgr AFTER INSERT ON booking.reservation FOR EACH ROW EXECUTE PROCEDURE booking.mat_summary_create ('reservation'); +CREATE TRIGGER mat_summary_change_tgr AFTER UPDATE ON booking.reservation FOR EACH ROW EXECUTE PROCEDURE booking.mat_summary_update (); +CREATE TRIGGER mat_summary_remove_tgr AFTER DELETE ON booking.reservation FOR EACH ROW EXECUTE PROCEDURE booking.mat_summary_delete (); + + CREATE TABLE booking.reservation_attr_value_map ( id SERIAL PRIMARY KEY, reservation INT NOT NULL diff --git a/Open-ILS/src/sql/Pg/100.circ_matrix.sql b/Open-ILS/src/sql/Pg/100.circ_matrix.sql index 2e79c9829..649262724 100644 --- a/Open-ILS/src/sql/Pg/100.circ_matrix.sql +++ b/Open-ILS/src/sql/Pg/100.circ_matrix.sql @@ -508,6 +508,12 @@ BEGIN SELECT SUM(f.balance_owed) INTO current_fines FROM money.materialized_billable_xact_summary f JOIN ( + SELECT r.id + FROM booking.reservation r + JOIN actor.org_unit_full_path( max_fines.org_unit ) fp ON (r.pickup_lib = fp.id) + WHERE usr = match_user + AND xact_finish IS NULL + UNION ALL SELECT g.id FROM money.grocery g JOIN actor.org_unit_full_path( max_fines.org_unit ) fp ON (g.billing_location = fp.id) @@ -688,6 +694,12 @@ BEGIN SELECT SUM(f.balance_owed) INTO current_fines FROM money.materialized_billable_xact_summary f JOIN ( + SELECT r.id + FROM booking.reservation r + JOIN actor.org_unit_full_path( max_fines.org_unit ) fp ON (r.pickup_lib = fp.id) + WHERE usr = match_user + AND xact_finish IS NULL + UNION ALL SELECT g.id FROM money.grocery g JOIN actor.org_unit_full_path( max_fines.org_unit ) fp ON (g.billing_location = fp.id) diff --git a/Open-ILS/src/sql/Pg/500.view.cross-schema.sql b/Open-ILS/src/sql/Pg/500.view.cross-schema.sql index fbc46ffc2..4909e8219 100644 --- a/Open-ILS/src/sql/Pg/500.view.cross-schema.sql +++ b/Open-ILS/src/sql/Pg/500.view.cross-schema.sql @@ -20,7 +20,7 @@ BEGIN; CREATE OR REPLACE VIEW money.open_billable_xact_summary AS SELECT xact.id AS id, xact.usr AS usr, - COALESCE(circ.circ_lib,groc.billing_location) AS billing_location, + COALESCE(circ.circ_lib,groc.billing_location,res.pickup_lib) AS billing_location, xact.xact_start AS xact_start, xact.xact_finish AS xact_finish, SUM(credit.amount) AS total_paid, @@ -37,6 +37,7 @@ CREATE OR REPLACE VIEW money.open_billable_xact_summary AS JOIN pg_class p ON (xact.tableoid = p.oid) LEFT JOIN "action".circulation circ ON (circ.id = xact.id) LEFT JOIN money.grocery groc ON (groc.id = xact.id) + LEFT JOIN booking.reservation res ON (groc.id = xact.id) LEFT JOIN ( SELECT billing.xact, billing.voided, diff --git a/Open-ILS/src/sql/Pg/upgrade/0105.schema.booking-integration.sql b/Open-ILS/src/sql/Pg/upgrade/0105.schema.booking-integration.sql new file mode 100644 index 000000000..4b96afab5 --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/0105.schema.booking-integration.sql @@ -0,0 +1,339 @@ +BEGIN; + +INSERT INTO config.upgrade_log (version) VALUES ('0105'); -- miker + +CREATE TRIGGER mat_summary_create_tgr AFTER INSERT ON booking.reservation FOR EACH ROW EXECUTE PROCEDURE booking.mat_summary_create ('reservation'); +CREATE TRIGGER mat_summary_change_tgr AFTER UPDATE ON booking.reservation FOR EACH ROW EXECUTE PROCEDURE booking.mat_summary_update (); +CREATE TRIGGER mat_summary_remove_tgr AFTER DELETE ON booking.reservation FOR EACH ROW EXECUTE PROCEDURE booking.mat_summary_delete (); + +CREATE OR REPLACE VIEW money.open_billable_xact_summary AS + SELECT xact.id AS id, + xact.usr AS usr, + COALESCE(circ.circ_lib,groc.billing_location,res.pickup_lib) AS billing_location, + xact.xact_start AS xact_start, + xact.xact_finish AS xact_finish, + SUM(credit.amount) AS total_paid, + MAX(credit.payment_ts) AS last_payment_ts, + LAST(credit.note) AS last_payment_note, + LAST(credit.payment_type) AS last_payment_type, + SUM(debit.amount) AS total_owed, + MAX(debit.billing_ts) AS last_billing_ts, + LAST(debit.note) AS last_billing_note, + LAST(debit.billing_type) AS last_billing_type, + COALESCE(SUM(debit.amount),0) - COALESCE(SUM(credit.amount),0) AS balance_owed, + p.relname AS xact_type + FROM money.billable_xact xact + JOIN pg_class p ON (xact.tableoid = p.oid) + LEFT JOIN "action".circulation circ ON (circ.id = xact.id) + LEFT JOIN money.grocery groc ON (groc.id = xact.id) + LEFT JOIN booking.reservation res ON (groc.id = xact.id) + LEFT JOIN ( + SELECT billing.xact, + billing.voided, + sum(billing.amount) AS amount, + max(billing.billing_ts) AS billing_ts, + last(billing.note) AS note, + last(billing.billing_type) AS billing_type + FROM money.billing + WHERE billing.voided IS FALSE + GROUP BY billing.xact, billing.voided + ) debit ON (xact.id = debit.xact AND debit.voided IS FALSE) + LEFT JOIN ( + SELECT payment_view.xact, + payment_view.voided, + sum(payment_view.amount) AS amount, + max(payment_view.payment_ts) AS payment_ts, + last(payment_view.note) AS note, + last(payment_view.payment_type) AS payment_type + FROM money.payment_view + WHERE payment_view.voided IS FALSE + GROUP BY payment_view.xact, payment_view.voided + ) credit ON (xact.id = credit.xact AND credit.voided IS FALSE) + WHERE xact.xact_finish IS NULL + GROUP BY 1,2,3,4,5,15 + ORDER BY MAX(debit.billing_ts), MAX(credit.payment_ts); + +CREATE OR REPLACE FUNCTION actor.calculate_system_penalties( match_user INT, context_org INT ) RETURNS SETOF actor.usr_standing_penalty AS $func$ +DECLARE + user_object actor.usr%ROWTYPE; + new_sp_row actor.usr_standing_penalty%ROWTYPE; + existing_sp_row actor.usr_standing_penalty%ROWTYPE; + collections_fines permission.grp_penalty_threshold%ROWTYPE; + max_fines permission.grp_penalty_threshold%ROWTYPE; + max_overdue permission.grp_penalty_threshold%ROWTYPE; + max_items_out permission.grp_penalty_threshold%ROWTYPE; + tmp_grp INT; + items_overdue INT; + items_out INT; + context_org_list INT[]; + current_fines NUMERIC(8,2) := 0.0; + tmp_fines NUMERIC(8,2); + tmp_groc RECORD; + tmp_circ RECORD; + tmp_org actor.org_unit%ROWTYPE; +BEGIN + SELECT INTO user_object * FROM actor.usr WHERE id = match_user; + + -- Max fines + SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org; + + -- Fail if the user has a high fine balance + LOOP + tmp_grp := user_object.profile; + LOOP + SELECT * INTO max_fines FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 1 AND org_unit = tmp_org.id; + + IF max_fines.threshold IS NULL THEN + SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp; + ELSE + EXIT; + END IF; + + IF tmp_grp IS NULL THEN + EXIT; + END IF; + END LOOP; + + IF max_fines.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN + EXIT; + END IF; + + SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou; + + END LOOP; + + IF max_fines.threshold IS NOT NULL THEN + + FOR existing_sp_row IN + SELECT * + FROM actor.usr_standing_penalty + WHERE usr = match_user + AND org_unit = max_fines.org_unit + AND (stop_date IS NULL or stop_date > NOW()) + AND standing_penalty = 1 + LOOP + RETURN NEXT existing_sp_row; + END LOOP; + + SELECT SUM(f.balance_owed) INTO current_fines + FROM money.materialized_billable_xact_summary f + JOIN ( + SELECT r.id + FROM booking.reservation r + JOIN actor.org_unit_full_path( max_fines.org_unit ) fp ON (r.pickup_lib = fp.id) + WHERE usr = match_user + AND xact_finish IS NULL + UNION ALL + SELECT g.id + FROM money.grocery g + JOIN actor.org_unit_full_path( max_fines.org_unit ) fp ON (g.billing_location = fp.id) + WHERE usr = match_user + AND xact_finish IS NULL + UNION ALL + SELECT circ.id + FROM action.circulation circ + JOIN actor.org_unit_full_path( max_fines.org_unit ) fp ON (circ.circ_lib = fp.id) + WHERE usr = match_user + AND xact_finish IS NULL ) l USING (id); + + IF current_fines >= max_fines.threshold THEN + new_sp_row.usr := match_user; + new_sp_row.org_unit := max_fines.org_unit; + new_sp_row.standing_penalty := 1; + RETURN NEXT new_sp_row; + END IF; + END IF; + + -- Start over for max overdue + SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org; + + -- Fail if the user has too many overdue items + LOOP + tmp_grp := user_object.profile; + LOOP + + SELECT * INTO max_overdue FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 2 AND org_unit = tmp_org.id; + + IF max_overdue.threshold IS NULL THEN + SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp; + ELSE + EXIT; + END IF; + + IF tmp_grp IS NULL THEN + EXIT; + END IF; + END LOOP; + + IF max_overdue.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN + EXIT; + END IF; + + SELECT INTO tmp_org * FROM actor.org_unit WHERE id = tmp_org.parent_ou; + + END LOOP; + + IF max_overdue.threshold IS NOT NULL THEN + + FOR existing_sp_row IN + SELECT * + FROM actor.usr_standing_penalty + WHERE usr = match_user + AND org_unit = max_overdue.org_unit + AND (stop_date IS NULL or stop_date > NOW()) + AND standing_penalty = 2 + LOOP + RETURN NEXT existing_sp_row; + END LOOP; + + SELECT INTO items_overdue COUNT(*) + FROM action.circulation circ + JOIN actor.org_unit_full_path( max_overdue.org_unit ) fp ON (circ.circ_lib = fp.id) + WHERE circ.usr = match_user + AND circ.checkin_time IS NULL + AND circ.due_date < NOW() + AND (circ.stop_fines = 'MAXFINES' OR circ.stop_fines IS NULL); + + IF items_overdue >= max_overdue.threshold::INT THEN + new_sp_row.usr := match_user; + new_sp_row.org_unit := max_overdue.org_unit; + new_sp_row.standing_penalty := 2; + RETURN NEXT new_sp_row; + END IF; + END IF; + + -- Start over for max out + SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org; + + -- Fail if the user has too many checked out items + LOOP + tmp_grp := user_object.profile; + LOOP + SELECT * INTO max_items_out FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 3 AND org_unit = tmp_org.id; + + IF max_items_out.threshold IS NULL THEN + SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp; + ELSE + EXIT; + END IF; + + IF tmp_grp IS NULL THEN + EXIT; + END IF; + END LOOP; + + IF max_items_out.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN + EXIT; + END IF; + + SELECT INTO tmp_org * FROM actor.org_unit WHERE id = tmp_org.parent_ou; + + END LOOP; + + + -- Fail if the user has too many items checked out + IF max_items_out.threshold IS NOT NULL THEN + + FOR existing_sp_row IN + SELECT * + FROM actor.usr_standing_penalty + WHERE usr = match_user + AND org_unit = max_items_out.org_unit + AND (stop_date IS NULL or stop_date > NOW()) + AND standing_penalty = 3 + LOOP + RETURN NEXT existing_sp_row; + END LOOP; + + SELECT INTO items_out COUNT(*) + FROM action.circulation circ + JOIN actor.org_unit_full_path( max_items_out.org_unit ) fp ON (circ.circ_lib = fp.id) + WHERE circ.usr = match_user + AND circ.checkin_time IS NULL + AND (circ.stop_fines IN ('MAXFINES','LONGOVERDUE') OR circ.stop_fines IS NULL); + + IF items_out >= max_items_out.threshold::INT THEN + new_sp_row.usr := match_user; + new_sp_row.org_unit := max_items_out.org_unit; + new_sp_row.standing_penalty := 3; + RETURN NEXT new_sp_row; + END IF; + END IF; + + -- Start over for collections warning + SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org; + + -- Fail if the user has a collections-level fine balance + LOOP + tmp_grp := user_object.profile; + LOOP + SELECT * INTO max_fines FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 4 AND org_unit = tmp_org.id; + + IF max_fines.threshold IS NULL THEN + SELECT parent INTO tmp_grp FROM permission.grp_tree WHERE id = tmp_grp; + ELSE + EXIT; + END IF; + + IF tmp_grp IS NULL THEN + EXIT; + END IF; + END LOOP; + + IF max_fines.threshold IS NOT NULL OR tmp_org.parent_ou IS NULL THEN + EXIT; + END IF; + + SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou; + + END LOOP; + + IF max_fines.threshold IS NOT NULL THEN + + FOR existing_sp_row IN + SELECT * + FROM actor.usr_standing_penalty + WHERE usr = match_user + AND org_unit = max_fines.org_unit + AND (stop_date IS NULL or stop_date > NOW()) + AND standing_penalty = 4 + LOOP + RETURN NEXT existing_sp_row; + END LOOP; + + SELECT SUM(f.balance_owed) INTO current_fines + FROM money.materialized_billable_xact_summary f + JOIN ( + SELECT r.id + FROM booking.reservation r + JOIN actor.org_unit_full_path( max_fines.org_unit ) fp ON (r.pickup_lib = fp.id) + WHERE usr = match_user + AND xact_finish IS NULL + UNION ALL + SELECT g.id + FROM money.grocery g + JOIN actor.org_unit_full_path( max_fines.org_unit ) fp ON (g.billing_location = fp.id) + WHERE usr = match_user + AND xact_finish IS NULL + UNION ALL + SELECT circ.id + FROM action.circulation circ + JOIN actor.org_unit_full_path( max_fines.org_unit ) fp ON (circ.circ_lib = fp.id) + WHERE usr = match_user + AND xact_finish IS NULL ) l USING (id); + + IF current_fines >= max_fines.threshold THEN + new_sp_row.usr := match_user; + new_sp_row.org_unit := max_fines.org_unit; + new_sp_row.standing_penalty := 4; + RETURN NEXT new_sp_row; + END IF; + END IF; + + + RETURN; +END; +$func$ LANGUAGE plpgsql; + +COMMIT; + diff --git a/Open-ILS/src/support-scripts/test-scripts/collections.pl b/Open-ILS/src/support-scripts/test-scripts/collections.pl index 4dcf77d8c..5b3b516be 100644 --- a/Open-ILS/src/support-scripts/test-scripts/collections.pl +++ b/Open-ILS/src/support-scripts/test-scripts/collections.pl @@ -80,6 +80,7 @@ for my $d (@$user_data) { my $user = $xact_data->{usr}->{__data__}; my $circs = $xact_data->{transactions}->{circulations}; my $grocery = $xact_data->{transactions}->{grocery}; + my $reservations = $xact_data->{transactions}->{reservations}; # -------------------------------------------------------------------- @@ -96,7 +97,7 @@ for my $d (@$user_data) { $a->{post_code}) . "\n"; } - print_xact_details($_->{__data__}) for (@$circs, @$grocery); + print_xact_details($_->{__data__}) for (@$circs, @$grocery, @$reservations); print "\n" . '-'x60 . "\n"; } diff --git a/Open-ILS/web/opac/skin/default/js/myopac.js b/Open-ILS/web/opac/skin/default/js/myopac.js index 7f494424f..7b596124a 100644 --- a/Open-ILS/web/opac/skin/default/js/myopac.js +++ b/Open-ILS/web/opac/skin/default/js/myopac.js @@ -616,6 +616,12 @@ function myOPACShowTransactions(r) { else if(trans.xact_type() == 'grocery' ) myopacShowGenericTransaction( trans ); + +/* XXX need to copy circulation output function here + else if(trans.xact_type() == 'reservation' ) + myopacShowReservationTransaction( trans ); +*/ + } } diff --git a/Open-ILS/xul/staff_client/chrome/content/main/constants.js b/Open-ILS/xul/staff_client/chrome/content/main/constants.js index 6aae3668d..1668bb66a 100644 --- a/Open-ILS/xul/staff_client/chrome/content/main/constants.js +++ b/Open-ILS/xul/staff_client/chrome/content/main/constants.js @@ -230,6 +230,7 @@ const api = { 'FM_MP_NOTE_EDIT' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.money.payment.note.edit' }, 'FM_MG_CREATE' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.money.grocery.create' }, 'FM_MG_RETRIEVE' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.money.grocery.retrieve' }, + 'FM_BRESV_RETRIEVE' : { 'app' : 'open-ils.circ', 'method' : 'open-ils.circ.booking.reservation.retrieve' }, 'FM_MOBTS_HAVING_BALANCE' : { 'app' : 'open-ils.actor', 'method' : 'open-ils.actor.user.transactions.have_balance' }, 'FM_MOBTS_HAVING_BALANCE.authoritative' : { 'app' : 'open-ils.actor', 'method' : 'open-ils.actor.user.transactions.have_balance.authoritative' }, 'FM_MOBTS_TOTAL_HAVING_BALANCE' : { 'app' : 'open-ils.actor', 'method' : 'open-ils.actor.user.transactions.have_balance.total' }, diff --git a/Open-ILS/xul/staff_client/server/patron/bill_details.js b/Open-ILS/xul/staff_client/server/patron/bill_details.js index f479c4d7d..1b5bca24c 100644 --- a/Open-ILS/xul/staff_client/server/patron/bill_details.js +++ b/Open-ILS/xul/staff_client/server/patron/bill_details.js @@ -30,6 +30,7 @@ function retrieve_mbts() { switch(g.mbts.xact_type()) { case 'circulation' : retrieve_circ(); break; case 'grocery' : retrieve_grocery(); $('copy_summary_vbox').hidden = true; $('copy_summary_splitter').hidden = true; break; + case 'reservation' : retrieve_reservation(); $('copy_summary_vbox').hidden = true; $('copy_summary_splitter').hidden = true; break; default: $('copy_summary_vbox').hidden = true; $('copy_summary_splitter').hidden = true; break; } @@ -52,6 +53,18 @@ function retrieve_grocery() { ); } +function retrieve_reservation() { + JSAN.use('util.widgets'); + g.network.simple_request('FM_BRESV_RETRIEVE', [ ses(), g.mbts_id ], + function (req) { + var r_bresv = req.getResultObject(); + if (instanceOf(r_bresv,bresv)) { + $('billing_location').value = g.data.hash.aou[ r_bresv.pickup_lib() ].shortname() + ' : ' + g.data.hash.aou[ r_bresv.pickup_lib() ].name(); + } + } + ); +} + function retrieve_circ() { JSAN.use('util.widgets'); g.network.simple_request('FM_CIRC_RETRIEVE_VIA_ID', [ ses(), g.mbts_id ],