],
mbt => [
'circulation',
- 'grocery'
+ 'grocery',
+ 'reservation'
]
}
}
__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'
);
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
# not just a generic payment object
for my $xact (
@{$blob->{transactions}->{circulations}},
+ @{$blob->{transactions}->{reservations}},
@{$blob->{transactions}->{grocery}} ) {
my $ps;
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;
+}
+
# --------------------------------------------------------------
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();
}
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;
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' );
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' );
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' );
--- /dev/null
+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;
+
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' );
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;
}
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);
}
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");
$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;
}
$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'}
);
$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...
$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;
}
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');
}
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);
# 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 {
$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,
}
} 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);
};
$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;
}
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";
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
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
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)
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,
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);
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,
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
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)
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)
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,
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,
--- /dev/null
+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;
+
my $user = $xact_data->{usr}->{__data__};
my $circs = $xact_data->{transactions}->{circulations};
my $grocery = $xact_data->{transactions}->{grocery};
+ my $reservations = $xact_data->{transactions}->{reservations};
# --------------------------------------------------------------------
$a->{post_code}) . "\n";
}
- print_xact_details($_->{__data__}) for (@$circs, @$grocery);
+ print_xact_details($_->{__data__}) for (@$circs, @$grocery, @$reservations);
print "\n" . '-'x60 . "\n";
}
else if(trans.xact_type() == 'grocery' )
myopacShowGenericTransaction( trans );
+
+/* XXX need to copy circulation output function here
+ else if(trans.xact_type() == 'reservation' )
+ myopacShowReservationTransaction( trans );
+*/
+
}
}
'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' },
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;
}
);
}
+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 ],