+++ /dev/null
-#!/bin/bash
-
-# Copyright (C) 2014 Georgia Public Library Service
-# Chris Sharp <csharp@georgialibraries.org>
-#
-# A program that takes a library system or branch name and a date or set of dates
-# on the command line to apply a batch void for billings applied on that day.
-# This is particularly useful if closed dates are set after the fine generator has
-# run for that day.
-#
-# This script assumes that you are on a server with direct access to your master
-# database, and that you have set the credentials for that database in the .pgpass
-# file in your user's home directory.
-#
-# See http://www.postgresql.org/docs/9.1/static/libpq-pgpass.html for more information.
-#
-# Usage Notes:
-#
-# You'll want to set the $EGUSER/$EGPASS variables to the username/password of an Evergreen
-# user (e.g., your administrative user in Evergreen). Adjust the $SRFSH variable to suit
-# your environment. As of this writing, we're still installing Evergreen in /openils.
-# The $VOID_NOTE variable should be something informative to your staff users as to why these
-# bills were voided. Our use case was closings due to inclement weather.
-#
-# The $BACKUP_SCHEMA variable should be set to a schema in your postgresql database that is
-# for administrative use. You should NOT use an existing Evergreen schema name (e.g. "action",
-# "actor", etc. for this purpose. I use "csharp" for that in our case.
-#
-# My hope is that this can be re-implemented in Perl or something with actual OpenSRF bindings,
-# but my current skill level and available time prevented me from doing so now.
-
-EGUSER="myuser"
-EGPASS="mypass"
-SRFSH="/openils/bin/srfsh"
-DBHOST="mydbhost"
-DBUSER="mydbuser"
-BACKUP_SCHEMA="myschema"
-
-Usage() { echo "Usage: ./batch_void_fines.sh -d YYYY-MM-DD,YYYY-MM-DD,... -s systemname OR -b branchname."
-}
-
-while getopts s:b:d:n:h OPTIONS
-do case "$OPTIONS" in
- s) SYSTEM="$OPTARG";;
- b) BRANCH="$OPTARG";;
- d) DATE="$OPTARG";;
- n) VOID_NOTE="$OPTARG";;
- h) Usage ; exit 1;;
- *) Usage ; exit 2;;
- esac
-done
-
-if [ -n "$SYSTEM" ] && [ -n "$BRANCH" ]; then
- echo "Please only specify either system OR branch. Not both."
- Usage
- exit 1;
-fi
-
-if [ -n "$VOID_NOTE" ]; then
-VOID_NOTE="VOIDED BY PINES STAFF BY REQUEST FROM LIBRARY/SYSTEM"
-fi
-
-if [ -z "$DATE" ]; then
- echo "Date is required."
- Usage
- exit 1;
-fi
-
-if [ ! $(echo $DATE | egrep '20[0-9][0-9]-[01][0-9]-[0-3][0-9](,20[0-9][0-9]-[01][0-9]-[0-3][0-9])?') ]; then
- echo "Date must be in YYYY-MM-DD format. Please check."
- Usage
- exit 1;
-fi
-
-if [ $(echo $DATE | grep ",") ]; then
- SPLITDATE=`echo $DATE | sed "s/,/', '/g"`
- DATE=$SPLITDATE
-fi
-
-if [ -n "$SYSTEM" ]; then
- if [ $(echo $SYSTEM | grep "-") ]; then
- echo "System names do not have hyphens. Did you mean -b?"
- Usage
- exit 1;
- fi
- BACKUP_TABLE="$BACKUP_SCHEMA.`echo $SYSTEM | tr '[:upper:]' '[:lower:]'`_batch_voided_fines_`echo $DATE | tr '-' '_' | sed "s/'//g" | sed "s/, /_/g"`"
-else
- if [ ! $(echo $BRANCH | grep "-") ]; then
- echo "Branch names have hyphens. Did you mean -s?"
- Usage
- exit 1;
- fi
- BACKUP_TABLE="$BACKUP_SCHEMA.`echo $BRANCH | tr '[:upper:]' '[:lower:]' | tr '-' '_'`_batch_voided_fines_`echo $DATE | tr '-' '_' | sed "s/'//g" | sed "s/, /_/g"`"
-fi
-read -d '' SYSTEM_Q <<END_OF_Q
-SELECT * INTO $BACKUP_TABLE
-FROM money.billing mb
-WHERE btype = 1
-AND date(billing_ts) IN (
- '$DATE'
-) AND voided = FALSE
- AND EXISTS (
- SELECT 1
- FROM action.circulation ac
- WHERE mb.xact = ac.id
- AND ac.circ_lib IN (
- SELECT id
- FROM actor.org_unit
- WHERE parent_ou IN (
- SELECT id
- FROM actor.org_unit
- WHERE shortname = '$SYSTEM')));
-END_OF_Q
-read -d '' BRANCH_Q <<END_OF_Q
-SELECT * INTO $BACKUP_TABLE
-FROM money.billing mb
-WHERE btype = 1
-AND date(billing_ts) IN (
- '$DATE'
-) AND voided = FALSE
- AND EXISTS (
- SELECT 1
- FROM action.circulation ac
- WHERE mb.xact = ac.id
- AND ac.circ_lib IN (
- SELECT id
- FROM actor.org_unit
- WHERE shortname = '$BRANCH'));
-END_OF_Q
-
-if [ -n "$SYSTEM" ]; then
- echo "$SYSTEM_Q"
- psql -U "$DBUSER" -h "$DBHOST" -c "$SYSTEM_Q"
-fi
-
-if [ -n "$BRANCH" ]; then
- echo "$BRANCH_Q"
- psql -U "$DBUSER" -h "$DBHOST" -c "$BRANCH_Q"
-fi
-
-read -d '' BILLS_SQL <<END_OF_Q
-SELECT id FROM $BACKUP_TABLE;
-END_OF_Q
-
-read -d '' COUNT_SQL <<END_OF_Q
-SELECT count(*)
-FROM money.billing
-WHERE NOT voided
-AND id IN (
- SELECT id
- FROM $BACKUP_TABLE
-);
-END_OF_Q
-
-read -d '' UPDATE_SQL <<END_OF_Q
-BEGIN;
-UPDATE money.billing
-SET note = '$VOID_NOTE'
-WHERE id IN (
- SELECT id
- FROM $BACKUP_TABLE
-);
-COMMIT;
-END_OF_Q
-
-BILLS=`psql -A -t -U "$DBUSER" -h "$DBHOST" -c "$BILLS_SQL" | sed 's/^/"/g' | sed 's/$/", /g' | tr '\n' ' '`
-
-AUTHTOKEN=`echo "login $EGUSER $EGPASS" | $SRFSH | grep "Login Session" | cut -d':' -f 2 | cut -d'.' -f1 | sed 's/ //g'`
-
-SRFSH_COMMAND="request open-ils.circ open-ils.circ.money.billing.void \"$AUTHTOKEN\" $BILLS"
-
-echo "$SRFSH_COMMAND" >> "${BACKUP_TABLE}_void.srfsh"
-
-echo "$SRFSH_COMMAND" | $SRFSH
-
-until [ $(psql -A -t -U "$DBUSER" -h "$DBHOST" -c "$COUNT_SQL") == "0" ]; do
- echo "Waiting for srfsh command to complete..."
- sleep 10
-done
-
-psql -U "$DBUSER" -h "$DBHOST" -c "$UPDATE_SQL"
-
-echo "Update complete"
--- /dev/null
+Batch Forgive Fines
+===================
+
+Create a table to work from
+---------------------------
+
+- Example 1: Forgive all fines/fees resulting from circulations at Thomas County Public Library:
+
+`select x.id as xact_id, u.last_xact_id as usr_last_xact_id, u.id as usr_id, x.* into myschema.my_forgiven_fines_and_fees from money.materialized_billable_xact_summary x join action.circulation c on (x.id = c.id) join actor.usr u on (c.usr = u.id) join actor.org_unit cl on (c.circ_lib = cl.id) where cl.shortname ~ '^TCPLS' and x.balance_owed > '0.00';`
+
+- Example 2: Forgive just fines, excluding fees and lost/longoverdue charges:
+
+`select x.id as xact_id, u.last_xact_id as usr_last_xact_id, u.id as usr_id, x.* into myschema.my_forgiven_fines_only from money.materialized_billable_xact_summary x join action.circulation c on (x.id = c.id) join actor.usr u on (c.usr = u.id) join actor.org_unit cl on (c.circ_lib = cl.id) where cl.shortname ~ '^TCPLS' and x.balance_owed > '0.00' and x.last_billing_type ~* '^overdue';`
+
+- Example 3: Forgive "grocery" (staff-created) bills:
+
+`select x.id as xact_id, u.last_xact_id as usr_last_xact_id, u.id as usr_id, x.* into myschema.my_forgiven_grocery from money.materialized_billable_xact_summary x join money.grocery g on (x.id = g.id) join actor.usr u on (g.usr = u.id) join actor.org_unit gl on (g.billing_location = gl.id) where cl.shortname ~ '^TCPLS' and x.balance_owed > '0.00';`
+
+Adjust the Perl to include the table
+------------------------------------
+
+Edit `batch_forgive_fines.pl` and change the assignment of `$source_table` to be the name of the table you created above.
+
+Run the script
+--------------
+
+You need the username and password of a privileged Evergreen user (ex. "admin") and the name of a workstation (ex. "STATELIB-A-csharp-firefox"). Then run:
+
+`./batch_forgive_fines.pl <username> <password> <workstation>`
+
+substituting the actual username, password, and workstation name (no brackets).
--- /dev/null
+#!/usr/bin/perl
+# ---------------------------------------------------------------
+# Copyright © 2014 Merrimack Valley Library Consortium
+# Jason Stephenson <jason@sigio.com>
+# Copyright (C) 2018 Georgia Public Library Service (Modifications)
+# Chris Sharp <csharp@georgialibraries.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+# ---------------------------------------------------------------
+
+use strict;
+use warnings;
+
+use OpenILS::Utils::Cronscript;
+use Data::Dumper;
+use DBI;
+
+my $source_table = "csharp.tcpls_circ_fines_purge";
+
+my $apputils = 'OpenILS::Application::AppUtils';
+
+my $script = OpenILS::Utils::Cronscript->new({nolockfile=>1});
+
+my $login = {
+ username => $ARGV[0],
+ password => $ARGV[1],
+ workstation => $ARGV[2],
+ type => 'staff'
+};
+
+my $authtoken = $script->authenticate($login);
+
+die "failed to authenticate" unless($authtoken);
+
+END {
+ $script->logout();
+}
+
+sub retrieve_usr_last_xact_id {
+ my $usr_id = shift;
+ my $e = $script->editor(authtoken=>$authtoken);
+ my $user = $e->retrieve_actor_user($usr_id) if ($usr_id);
+ return $e->event unless($user);
+ my $last_xact_id = $user->last_xact_id;
+ return $last_xact_id;
+}
+
+
+my $settings = {
+ host => "db01",
+ db => "evergreen",
+ user => "evergreen"
+};
+
+my $dbh = DBI->connect('DBI:Pg:dbname=' . $settings->{'db'} . ';host=' . $settings->{'host'}, $settings->{'user'},
+ undef,
+ {
+ RaiseError => 1,
+ ShowErrorStatement => 0,
+ AutoCommit => 0
+ }
+) or die DBI->errstr;
+
+my $query = "select xact_id, usr_last_xact_id, usr_id, sum(balance_owed) as balance_owed from $source_table where balance_owed > '0.00' group by 1, 2, 3";
+
+my ($xact_id, $usr_last_xact_id, $usr_id, $balance_owed);
+
+my $sth = $dbh->prepare($query);
+$sth->execute();
+my $ref = $sth->bind_columns(\($xact_id, $usr_last_xact_id, $usr_id, $balance_owed));
+
+while ($sth->fetch) {
+ print "applying forgive payment of $balance_owed to xact_id: $xact_id for usr_id: $usr_id\n";
+ my @payments = [$xact_id, $balance_owed];
+ $usr_last_xact_id = retrieve_usr_last_xact_id($usr_id);
+ my $result = $apputils->simplereq(
+ 'open-ils.circ',
+ 'open-ils.circ.money.payment',
+ $authtoken,
+ {
+ payment_type => "forgive_payment",
+ userid => $usr_id,
+ note => "Bills forgiven per TCPLS System Director - 4/9/2020",
+ payments => \@payments
+ },
+ $usr_last_xact_id
+ );
+ print Dumper $result;
+}
+
+$sth->finish();
+$dbh->disconnect();
--- /dev/null
+#!/bin/bash
+
+# Copyright (C) 2014 Georgia Public Library Service
+# Chris Sharp <csharp@georgialibraries.org>
+#
+# A program that takes a library system or branch name and a date or set of dates
+# on the command line to apply a batch void for billings applied on that day.
+# This is particularly useful if closed dates are set after the fine generator has
+# run for that day.
+#
+# This script assumes that you are on a server with direct access to your master
+# database, and that you have set the credentials for that database in the .pgpass
+# file in your user's home directory.
+#
+# See http://www.postgresql.org/docs/9.1/static/libpq-pgpass.html for more information.
+#
+# Usage Notes:
+#
+# You'll want to set the $EGUSER/$EGPASS variables to the username/password of an Evergreen
+# user (e.g., your administrative user in Evergreen). Adjust the $SRFSH variable to suit
+# your environment. As of this writing, we're still installing Evergreen in /openils.
+# The $VOID_NOTE variable should be something informative to your staff users as to why these
+# bills were voided. Our use case was closings due to inclement weather.
+#
+# The $BACKUP_SCHEMA variable should be set to a schema in your postgresql database that is
+# for administrative use. You should NOT use an existing Evergreen schema name (e.g. "action",
+# "actor", etc. for this purpose. I use "csharp" for that in our case.
+#
+# My hope is that this can be re-implemented in Perl or something with actual OpenSRF bindings,
+# but my current skill level and available time prevented me from doing so now.
+
+EGUSER="myuser"
+EGPASS="mypass"
+SRFSH="/openils/bin/srfsh"
+DBHOST="mydbhost"
+DBUSER="mydbuser"
+BACKUP_SCHEMA="myschema"
+
+Usage() { echo "Usage: ./batch_void_fines.sh -d YYYY-MM-DD,YYYY-MM-DD,... -s systemname OR -b branchname."
+}
+
+while getopts s:b:d:n:h OPTIONS
+do case "$OPTIONS" in
+ s) SYSTEM="$OPTARG";;
+ b) BRANCH="$OPTARG";;
+ d) DATE="$OPTARG";;
+ n) VOID_NOTE="$OPTARG";;
+ h) Usage ; exit 1;;
+ *) Usage ; exit 2;;
+ esac
+done
+
+if [ -n "$SYSTEM" ] && [ -n "$BRANCH" ]; then
+ echo "Please only specify either system OR branch. Not both."
+ Usage
+ exit 1;
+fi
+
+if [ -n "$VOID_NOTE" ]; then
+VOID_NOTE="VOIDED BY PINES STAFF BY REQUEST FROM LIBRARY/SYSTEM"
+fi
+
+if [ -z "$DATE" ]; then
+ echo "Date is required."
+ Usage
+ exit 1;
+fi
+
+if [ ! $(echo $DATE | egrep '20[0-9][0-9]-[01][0-9]-[0-3][0-9](,20[0-9][0-9]-[01][0-9]-[0-3][0-9])?') ]; then
+ echo "Date must be in YYYY-MM-DD format. Please check."
+ Usage
+ exit 1;
+fi
+
+if [ $(echo $DATE | grep ",") ]; then
+ SPLITDATE=`echo $DATE | sed "s/,/', '/g"`
+ DATE=$SPLITDATE
+fi
+
+if [ -n "$SYSTEM" ]; then
+ if [ $(echo $SYSTEM | grep "-") ]; then
+ echo "System names do not have hyphens. Did you mean -b?"
+ Usage
+ exit 1;
+ fi
+ BACKUP_TABLE="$BACKUP_SCHEMA.`echo $SYSTEM | tr '[:upper:]' '[:lower:]'`_batch_voided_fines_`echo $DATE | tr '-' '_' | sed "s/'//g" | sed "s/, /_/g"`"
+else
+ if [ ! $(echo $BRANCH | grep "-") ]; then
+ echo "Branch names have hyphens. Did you mean -s?"
+ Usage
+ exit 1;
+ fi
+ BACKUP_TABLE="$BACKUP_SCHEMA.`echo $BRANCH | tr '[:upper:]' '[:lower:]' | tr '-' '_'`_batch_voided_fines_`echo $DATE | tr '-' '_' | sed "s/'//g" | sed "s/, /_/g"`"
+fi
+read -d '' SYSTEM_Q <<END_OF_Q
+SELECT * INTO $BACKUP_TABLE
+FROM money.billing mb
+WHERE btype = 1
+AND date(billing_ts) IN (
+ '$DATE'
+) AND voided = FALSE
+ AND EXISTS (
+ SELECT 1
+ FROM action.circulation ac
+ WHERE mb.xact = ac.id
+ AND ac.circ_lib IN (
+ SELECT id
+ FROM actor.org_unit
+ WHERE parent_ou IN (
+ SELECT id
+ FROM actor.org_unit
+ WHERE shortname = '$SYSTEM')));
+END_OF_Q
+read -d '' BRANCH_Q <<END_OF_Q
+SELECT * INTO $BACKUP_TABLE
+FROM money.billing mb
+WHERE btype = 1
+AND date(billing_ts) IN (
+ '$DATE'
+) AND voided = FALSE
+ AND EXISTS (
+ SELECT 1
+ FROM action.circulation ac
+ WHERE mb.xact = ac.id
+ AND ac.circ_lib IN (
+ SELECT id
+ FROM actor.org_unit
+ WHERE shortname = '$BRANCH'));
+END_OF_Q
+
+if [ -n "$SYSTEM" ]; then
+ echo "$SYSTEM_Q"
+ psql -U "$DBUSER" -h "$DBHOST" -c "$SYSTEM_Q"
+fi
+
+if [ -n "$BRANCH" ]; then
+ echo "$BRANCH_Q"
+ psql -U "$DBUSER" -h "$DBHOST" -c "$BRANCH_Q"
+fi
+
+read -d '' BILLS_SQL <<END_OF_Q
+SELECT id FROM $BACKUP_TABLE;
+END_OF_Q
+
+read -d '' COUNT_SQL <<END_OF_Q
+SELECT count(*)
+FROM money.billing
+WHERE NOT voided
+AND id IN (
+ SELECT id
+ FROM $BACKUP_TABLE
+);
+END_OF_Q
+
+read -d '' UPDATE_SQL <<END_OF_Q
+BEGIN;
+UPDATE money.billing
+SET note = '$VOID_NOTE'
+WHERE id IN (
+ SELECT id
+ FROM $BACKUP_TABLE
+);
+COMMIT;
+END_OF_Q
+
+BILLS=`psql -A -t -U "$DBUSER" -h "$DBHOST" -c "$BILLS_SQL" | sed 's/^/"/g' | sed 's/$/", /g' | tr '\n' ' '`
+
+AUTHTOKEN=`echo "login $EGUSER $EGPASS" | $SRFSH | grep "Login Session" | cut -d':' -f 2 | cut -d'.' -f1 | sed 's/ //g'`
+
+SRFSH_COMMAND="request open-ils.circ open-ils.circ.money.billing.void \"$AUTHTOKEN\" $BILLS"
+
+echo "$SRFSH_COMMAND" >> "${BACKUP_TABLE}_void.srfsh"
+
+echo "$SRFSH_COMMAND" | $SRFSH
+
+until [ $(psql -A -t -U "$DBUSER" -h "$DBHOST" -c "$COUNT_SQL") == "0" ]; do
+ echo "Waiting for srfsh command to complete..."
+ sleep 10
+done
+
+psql -U "$DBUSER" -h "$DBHOST" -c "$UPDATE_SQL"
+
+echo "Update complete"