integrate the new booking.reservation billable transaction table with all the parts...
authormiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Thu, 3 Dec 2009 21:29:05 +0000 (21:29 +0000)
committermiker <miker@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Thu, 3 Dec 2009 21:29:05 +0000 (21:29 +0000)
git-svn-id: svn://svn.open-ils.org/ILS/trunk@15071 dcc99617-32d9-48b4-a31d-7c20da2025e4

19 files changed:
Open-ILS/src/perlmods/OpenILS/Application/Circ.pm
Open-ILS/src/perlmods/OpenILS/Application/Circ/Money.pm
Open-ILS/src/perlmods/OpenILS/Application/Collections.pm
Open-ILS/src/perlmods/OpenILS/Application/CreditCard.pm
Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI.pm
Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI/booking.pm [new file with mode: 0644]
Open-ILS/src/perlmods/OpenILS/Application/Storage/Driver/Pg/dbi.pm
Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/action.pm
Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher/money.pm
Open-ILS/src/perlmods/OpenILS/WWW/BadDebt.pm
Open-ILS/src/sql/Pg/002.schema.config.sql
Open-ILS/src/sql/Pg/095.schema.booking.sql
Open-ILS/src/sql/Pg/100.circ_matrix.sql
Open-ILS/src/sql/Pg/500.view.cross-schema.sql
Open-ILS/src/sql/Pg/upgrade/0105.schema.booking-integration.sql [new file with mode: 0644]
Open-ILS/src/support-scripts/test-scripts/collections.pl
Open-ILS/web/opac/skin/default/js/myopac.js
Open-ILS/xul/staff_client/chrome/content/main/constants.js
Open-ILS/xul/staff_client/server/patron/bill_details.js

index cdd5951..1fecadd 100644 (file)
@@ -1379,7 +1379,8 @@ sub user_payments_list {
                     ],
                     mbt => [
                         'circulation', 
-                        'grocery'
+                        'grocery',
+                        'reservation'
                     ]
                 }
             }
index 0102f4e..30e2a82 100644 (file)
@@ -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'
 );
index 050a9d1..8b72ae9 100644 (file)
@@ -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;
+}
+
 
 
 # --------------------------------------------------------------
index 55e3b5c..843540e 100644 (file)
@@ -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();
     }
index 1388d81..c2b0f56 100644 (file)
@@ -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 (file)
index 0000000..7a7bb36
--- /dev/null
@@ -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;
+
index 0620bd7..9939f5a 100644 (file)
        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' );
index 82eb65e..6785a75 100644 (file)
@@ -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);
                };
index b049a0a..e1a2f0a 100644 (file)
@@ -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,
index a5015ee..cb7c322 100644 (file)
@@ -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);
     
index 9975722..3166387 100644 (file)
@@ -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,
index 79dbd76..7218da4 100644 (file)
@@ -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
index 2e79c98..6492627 100644 (file)
@@ -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)
index fbc46ff..4909e82 100644 (file)
@@ -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 (file)
index 0000000..4b96afa
--- /dev/null
@@ -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;
+
index 4dcf77d..5b3b516 100644 (file)
@@ -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";
 }
index 7f49442..7b59612 100644 (file)
@@ -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 );
+*/
+
        }
 }
 
index 6aae366..1668bb6 100644 (file)
@@ -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' },
index f479c4d..1b5bca2 100644 (file)
@@ -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 ],