<event code='1235' textcode='INVALID_USER_XACT_ID'>
<desc xml:lang="en-US">While you were trying to make payments, this account's transaction history changed. Please go back and try again.</desc>
</event>
-
-
-
-
+ <event code='1236' textcode='PATRON_EXCEEDS_LOST_COUNT'>
+ <desc xml:lang="en-US">The patron has too many lost items.</desc>
+ </event>
<event code='1500' textcode='ACTION_CIRCULATION_NOT_FOUND'>
<desc xml:lang="en-US">
Someone attempted to retrieve a circulation object from the system and
max_fines permission.grp_penalty_threshold%ROWTYPE;
max_overdue permission.grp_penalty_threshold%ROWTYPE;
max_items_out permission.grp_penalty_threshold%ROWTYPE;
+ max_lost permission.grp_penalty_threshold%ROWTYPE;
tmp_grp INT;
items_overdue INT;
items_out INT;
+ items_lost INT;
context_org_list INT[];
current_fines NUMERIC(8,2) := 0.0;
tmp_fines NUMERIC(8,2);
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);
+ AND (circ.stop_fines IN (
+ SELECT 'MAXFINES'::TEXT
+ UNION ALL
+ SELECT 'LONGOVERDUE'::TEXT
+ UNION ALL
+ SELECT 'LOST'::TEXT
+ WHERE 'true' ILIKE
+ (
+ SELECT CASE
+ WHEN (SELECT value FROM actor.org_unit_ancestor_setting('circ.tally_lost', circ.circ_lib)) ILIKE 'true' THEN 'true'
+ ELSE 'false'
+ END
+ )
+ UNION ALL
+ SELECT 'CLAIMSRETURNED'::TEXT
+ WHERE 'false' ILIKE
+ (
+ SELECT CASE
+ WHEN (SELECT value FROM actor.org_unit_ancestor_setting('circ.do_not_tally_claims_returned', circ.circ_lib)) ILIKE 'true' THEN 'true'
+ ELSE 'false'
+ END
+ )
+ ) OR circ.stop_fines IS NULL)
+ AND xact_finish IS NULL;
IF items_out >= max_items_out.threshold::INT THEN
new_sp_row.usr := match_user;
END IF;
END IF;
+ -- Start over for max lost
+ SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+ -- Fail if the user has too many lost items
+ LOOP
+ tmp_grp := user_object.profile;
+ LOOP
+
+ SELECT * INTO max_lost FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 5 AND org_unit = tmp_org.id;
+
+ IF max_lost.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_lost.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_lost.threshold IS NOT NULL THEN
+
+ RETURN QUERY
+ SELECT *
+ FROM actor.usr_standing_penalty
+ WHERE usr = match_user
+ AND org_unit = max_lost.org_unit
+ AND (stop_date IS NULL or stop_date > NOW())
+ AND standing_penalty = 5;
+
+ SELECT INTO items_lost COUNT(*)
+ FROM action.circulation circ
+ JOIN actor.org_unit_full_path( max_lost.org_unit ) fp ON (circ.circ_lib = fp.id)
+ WHERE circ.usr = match_user
+ AND circ.checkin_time IS NULL
+ AND (circ.stop_fines = 'LOST')
+ AND xact_finish IS NULL;
+
+ IF items_lost >= max_lost.threshold::INT AND 0 < max_lost.threshold::INT THEN
+ new_sp_row.usr := match_user;
+ new_sp_row.org_unit := max_lost.org_unit;
+ new_sp_row.standing_penalty := 5;
+ 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;
VALUES (3,'PATRON_EXCEEDS_CHECKOUT_COUNT',oils_i18n_gettext(3, 'Patron exceeds max checked out item threshold', 'csp', 'label'),'CIRC|FULFILL', TRUE);
INSERT INTO config.standing_penalty (id,name,label,block_list,staff_alert)
VALUES (4,'PATRON_EXCEEDS_COLLECTIONS_WARNING',oils_i18n_gettext(4, 'Patron exceeds pre-collections warning fine threshold', 'csp', 'label'),'CIRC|FULFILL|HOLD|CAPTURE|RENEW', TRUE);
+INSERT INTO config.standing_penalty (id,name,label,block_list,staff_alert)
+ VALUES (5,'PATRON_EXCEEDS_LOST_COUNT',oils_i18n_gettext(5, 'Patron exceeds max lost item threshold', 'csp', 'label'),'CIRC|FULFILL|HOLD|CAPTURE|RENEW', TRUE);
INSERT INTO config.standing_penalty (id,name,label,staff_alert) VALUES (20,'ALERT_NOTE',oils_i18n_gettext(20, 'Alerting Note, no blocks', 'csp', 'label'),TRUE);
INSERT INTO config.standing_penalty (id,name,label) VALUES (21,'SILENT_NOTE',oils_i18n_gettext(21, 'Note, no blocks', 'csp', 'label'));
VALUES (1,1,2,10.0);
INSERT INTO permission.grp_penalty_threshold (grp,org_unit,penalty,threshold)
VALUES (1,1,3,10.0);
+INSERT INTO permission.grp_penalty_threshold (grp,org_unit,penalty,threshold)
+ VALUES (1,1,5,10.0);
SELECT SETVAL('permission.grp_penalty_threshold_id_seq'::TEXT, (SELECT MAX(id) FROM permission.grp_penalty_threshold));
'coust', 'description'),
'bool', null)
+,('circ.tally_lost', 'circ',
+ oils_i18n_gettext(
+ 'circ.tally_lost',
+ 'Include Lost circulations in lump sum tallies in Patron Display.',
+ 'coust',
+ 'label'),
+ oils_i18n_gettext(
+ 'circ.tally_lost',
+ 'In the Patron Display interface, the number of total active circulations for a given patron is presented in the Summary sidebar and underneath the Items Out navigation button. This setting will include Lost circulations as counting toward these tallies.',
+ 'coust',
+ 'description'),
+ 'bool', null)
+
,( 'circ.grace.extend', 'circ',
oils_i18n_gettext('circ.grace.extend',
'Auto-Extend Grace Periods',
--- /dev/null
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+INSERT INTO config.standing_penalty (id,name,label,block_list,staff_alert)
+ VALUES (5,'PATRON_EXCEEDS_LOST_COUNT',oils_i18n_gettext(5, 'Patron exceeds max lost item threshold', 'csp', 'label'),'CIRC|FULFILL|HOLD|CAPTURE|RENEW', TRUE);
+
+INSERT INTO config.org_unit_setting_type ( name, grp, label, description, datatype ) VALUES (
+ 'circ.tally_lost', 'circ',
+ oils_i18n_gettext(
+ 'circ.tally_lost',
+ 'Include Lost circulations in lump sum tallies in Patron Display.',
+ 'coust',
+ 'label'),
+ oils_i18n_gettext(
+ 'circ.tally_lost',
+ 'In the Patron Display interface, the number of total active circulations for a given patron is presented in the Summary sidebar and underneath the Items Out navigation button. This setting will include Lost circulations as counting toward these tallies.',
+ 'coust',
+ 'description'),
+ 'bool'
+);
+
+-- Function: actor.calculate_system_penalties(integer, integer)
+-- DROP FUNCTION actor.calculate_system_penalties(integer, integer);
+
+CREATE OR REPLACE FUNCTION actor.calculate_system_penalties(match_user integer, context_org integer)
+ RETURNS SETOF actor.usr_standing_penalty AS
+$BODY$
+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;
+ max_lost permission.grp_penalty_threshold%ROWTYPE;
+ tmp_grp INT;
+ items_overdue INT;
+ items_out INT;
+ items_lost 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;
+ tmp_penalty config.standing_penalty%ROWTYPE;
+ tmp_depth INTEGER;
+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
+
+ RETURN QUERY
+ 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;
+
+ SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( max_fines.org_unit );
+
+ SELECT SUM(f.balance_owed) INTO current_fines
+ FROM money.materialized_billable_xact_summary f
+ JOIN (
+ SELECT r.id
+ FROM booking.reservation r
+ WHERE r.usr = match_user
+ AND r.pickup_lib IN (SELECT * FROM unnest(context_org_list))
+ AND xact_finish IS NULL
+ UNION ALL
+ SELECT g.id
+ FROM money.grocery g
+ WHERE g.usr = match_user
+ AND g.billing_location IN (SELECT * FROM unnest(context_org_list))
+ AND xact_finish IS NULL
+ UNION ALL
+ SELECT circ.id
+ FROM action.circulation circ
+ WHERE circ.usr = match_user
+ AND circ.circ_lib IN (SELECT * FROM unnest(context_org_list))
+ 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
+
+ RETURN QUERY
+ 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;
+
+ 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
+ RETURN QUERY
+ 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;
+ 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 (
+ SELECT 'MAXFINES'::TEXT
+ UNION ALL
+ SELECT 'LONGOVERDUE'::TEXT
+ UNION ALL
+ SELECT 'LOST'::TEXT
+ WHERE 'true' ILIKE
+ (
+ SELECT CASE
+ WHEN (SELECT value FROM actor.org_unit_ancestor_setting('circ.tally_lost', circ.circ_lib)) ILIKE 'true' THEN 'true'
+ ELSE 'false'
+ END
+ )
+ UNION ALL
+ SELECT 'CLAIMSRETURNED'::TEXT
+ WHERE 'false' ILIKE
+ (
+ SELECT CASE
+ WHEN (SELECT value FROM actor.org_unit_ancestor_setting('circ.do_not_tally_claims_returned', circ.circ_lib)) ILIKE 'true' THEN 'true'
+ ELSE 'false'
+ END
+ )
+ ) OR circ.stop_fines IS NULL)
+ AND xact_finish 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 max lost
+ SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+ -- Fail if the user has too many lost items
+ LOOP
+ tmp_grp := user_object.profile;
+ LOOP
+ SELECT * INTO max_lost FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 5 AND org_unit = tmp_org.id;
+ IF max_lost.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_lost.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_lost.threshold IS NOT NULL THEN
+ RETURN QUERY
+ SELECT *
+ FROM actor.usr_standing_penalty
+ WHERE usr = match_user
+ AND org_unit = max_lost.org_unit
+ AND (stop_date IS NULL or stop_date > NOW())
+ AND standing_penalty = 5;
+
+ SELECT INTO items_lost COUNT(*)
+ FROM action.circulation circ
+ JOIN actor.org_unit_full_path( max_lost.org_unit ) fp ON (circ.circ_lib = fp.id)
+ WHERE circ.usr = match_user
+ AND circ.checkin_time IS NULL
+ AND (circ.stop_fines = 'LOST')
+ AND xact_finish IS NULL;
+
+ IF items_lost >= max_lost.threshold::INT AND 0 < max_lost.threshold::INT THEN
+ new_sp_row.usr := match_user;
+ new_sp_row.org_unit := max_lost.org_unit;
+ new_sp_row.standing_penalty := 5;
+ 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
+
+ RETURN QUERY
+ 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;
+
+ SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( max_fines.org_unit );
+
+ SELECT SUM(f.balance_owed) INTO current_fines
+ FROM money.materialized_billable_xact_summary f
+ JOIN (
+ SELECT r.id
+ FROM booking.reservation r
+ WHERE r.usr = match_user
+ AND r.pickup_lib IN (SELECT * FROM unnest(context_org_list))
+ AND r.xact_finish IS NULL
+ UNION ALL
+ SELECT g.id
+ FROM money.grocery g
+ WHERE g.usr = match_user
+ AND g.billing_location IN (SELECT * FROM unnest(context_org_list))
+ AND g.xact_finish IS NULL
+ UNION ALL
+ SELECT circ.id
+ FROM action.circulation circ
+ WHERE circ.usr = match_user
+ AND circ.circ_lib IN (SELECT * FROM unnest(context_org_list))
+ AND circ.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;
+
+ -- Start over for in collections
+ SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+
+ -- Remove the in-collections penalty if the user has paid down enough
+ -- This penalty is different, because this code is not responsible for creating
+ -- new in-collections penalties, only for removing them
+ LOOP
+ tmp_grp := user_object.profile;
+ LOOP
+ SELECT * INTO max_fines FROM permission.grp_penalty_threshold WHERE grp = tmp_grp AND penalty = 30 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
+
+ SELECT INTO context_org_list ARRAY_ACCUM(id) FROM actor.org_unit_full_path( max_fines.org_unit );
+
+ -- first, see if the user had paid down to the threshold
+ SELECT SUM(f.balance_owed) INTO current_fines
+ FROM money.materialized_billable_xact_summary f
+ JOIN (
+ SELECT r.id
+ FROM booking.reservation r
+ WHERE r.usr = match_user
+ AND r.pickup_lib IN (SELECT * FROM unnest(context_org_list))
+ AND r.xact_finish IS NULL
+ UNION ALL
+ SELECT g.id
+ FROM money.grocery g
+ WHERE g.usr = match_user
+ AND g.billing_location IN (SELECT * FROM unnest(context_org_list))
+ AND g.xact_finish IS NULL
+ UNION ALL
+ SELECT circ.id
+ FROM action.circulation circ
+ WHERE circ.usr = match_user
+ AND circ.circ_lib IN (SELECT * FROM unnest(context_org_list))
+ AND circ.xact_finish IS NULL ) l USING (id);
+
+ IF current_fines IS NULL OR current_fines <= max_fines.threshold THEN
+ -- patron has paid down enough
+
+ SELECT INTO tmp_penalty * FROM config.standing_penalty WHERE id = 30;
+
+ IF tmp_penalty.org_depth IS NOT NULL THEN
+
+ -- since this code is not responsible for applying the penalty, it can't
+ -- guarantee the current context org will match the org at which the penalty
+ --- was applied. search up the org tree until we hit the configured penalty depth
+ SELECT INTO tmp_org * FROM actor.org_unit WHERE id = context_org;
+ SELECT INTO tmp_depth depth FROM actor.org_unit_type WHERE id = tmp_org.ou_type;
+
+ WHILE tmp_depth >= tmp_penalty.org_depth LOOP
+
+ RETURN QUERY
+ SELECT *
+ FROM actor.usr_standing_penalty
+ WHERE usr = match_user
+ AND org_unit = tmp_org.id
+ AND (stop_date IS NULL or stop_date > NOW())
+ AND standing_penalty = 30;
+
+ IF tmp_org.parent_ou IS NULL THEN
+ EXIT;
+ END IF;
+
+ SELECT * INTO tmp_org FROM actor.org_unit WHERE id = tmp_org.parent_ou;
+ SELECT INTO tmp_depth depth FROM actor.org_unit_type WHERE id = tmp_org.ou_type;
+ END LOOP;
+
+ ELSE
+
+ -- no penalty depth is defined, look for exact matches
+
+ RETURN QUERY
+ 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 = 30;
+ END IF;
+
+ END IF;
+
+ END IF;
+
+ RETURN;
+END;
+$BODY$
+ LANGUAGE plpgsql VOLATILE
+ COST 100
+ ROWS 1000;
+
+COMMIT;
<!ENTITY staff.patron.display_overlay.see_notes.value "(See Notes)">
<!ENTITY staff.patron.display_overlay.max_bills.value "(Maximum Bills)">
<!ENTITY staff.patron.display_overlay.max_overdues.value "(Maximum Overdues)">
+<!ENTITY staff.patron.display_overlay.max_lost.value "(Maximum Lost)">
<!ENTITY staff.patron.display_overlay.max_checked_out.value "(Maximum Checked Out)">
<!ENTITY staff.patron.display_overlay.has_bills.value "(Has Bills)">
<!ENTITY staff.patron.display_overlay.has_overdues.value "(Has Overdues)">
+<!ENTITY staff.patron.display_overlay.has_lost.value "(Has Lost)">
<!ENTITY staff.patron.display_overlay.invalid_dob.value "(Invalid Date of Birth)">
<!ENTITY staff.patron.display_overlay.invalid_address.value "(Invalid Address)">
<!ENTITY staff.patron.display_overlay.invalid_email.value "(Invalid Email)">
case 1232 /* ITEM_DEPOSIT_REQUIRED */ :
case 1233 /* ITEM_RENTAL_FEE_REQUIRED */ :
case 1234 /* ITEM_DEPOSIT_PAID */ :
+ case 1236 /* PATRON_EXCEEDS_LOST_COUNT */ :
case 1500 /* ACTION_CIRCULATION_NOT_FOUND */ :
case 7002 /* PATRON_EXCEEDS_CHECKOUT_COUNT */ :
case 7003 /* COPY_CIRC_NOT_ALLOWED */ :
1215 /* CIRC_EXCEEDS_COPY_RANGE */,
1232 /* ITEM_DEPOSIT_REQUIRED */,
1233 /* ITEM_RENTAL_FEE_REQUIRED */,
+ 1236 /* PATRON_EXCEEDS_LOST_COUNT */,
7002 /* PATRON_EXCEEDS_CHECKOUT_COUNT */,
7003 /* COPY_CIRC_NOT_ALLOWED */,
7004 /* COPY_NOT_AVAILABLE */,
'report_override_on_events' : [ /* Allow auto-override of Patron overrides only */
1212 /* PATRON_EXCEEDS_OVERDUE_COUNT */,
1213 /* PATRON_BARRED */,
+ 1236 /* PATRON_EXCEEDS_LOST_COUNT */,
7002 /* PATRON_EXCEEDS_CHECKOUT_COUNT */,
7013 /* PATRON_EXCEEDS_FINES */
],
'1233' : function(r) {
return document.getElementById('circStrings').getString('staff.circ.checkout.override.item_rental_fee_required.warning');
},
+ '1236' : function(r) {
+ return document.getElementById('circStrings').getString('staff.circ.checkout.override.will_auto');
+ },
'7002' : function(r) {
return document.getElementById('circStrings').getString('staff.circ.checkout.override.will_auto');
},
break;
case 1232 /* ITEM_DEPOSIT_REQUIRED */ :
case 1233 /* ITEM_RENTAL_FEE_REQUIRED */ :
+ case 1236 /* PATRON_EXCEEDS_LOST_COUNT */ :
+ found_handled = true;
+ break;
case 7013 /* PATRON_EXCEEDS_FINES */ :
found_handled = true;
break;
case 1232 /* ITEM_DEPOSIT_REQUIRED */ : break;
case 1233 /* ITEM_RENTAL_FEE_REQUIRED */ : break;
case 1234 /* ITEM_DEPOSIT_PAID */ : break;
+ case 1236 /* PATRON_EXCEEDS_LOST_COUNT */ : break;
case 1500 /* ACTION_CIRCULATION_NOT_FOUND */ : break;
case 1502 /* ASSET_COPY_NOT_FOUND */ :
var mis_scan_msg = document.getElementById('circStrings').getFormattedString('staff.circ.copy_status.status.copy_not_found', [params.barcode]);
1232 /* ITEM_DEPOSIT_REQUIRED */,
1233 /* ITEM_RENTAL_FEE_REQUIRED */,
1234 /* ITEM_DEPOSIT_PAID */,
+ 1236 /* PATRON_EXCEEDS_LOST_COUNT */,
7002 /* PATRON_EXCEEDS_CHECKOUT_COUNT */,
7003 /* COPY_CIRC_NOT_ALLOWED */,
7004 /* COPY_NOT_AVAILABLE */,
'1234' : function(r) {
return document.getElementById('circStrings').getFormattedString('staff.circ.utils.checkin.override.item_deposit_paid.warning');
},
+ '1236' : function(r) { return document.getElementById('circStrings').getFormattedString('staff.circ.renew.barcode', [params.barcode]); },
'7002' : function(r) { return document.getElementById('circStrings').getFormattedString('staff.circ.renew.barcode', [params.barcode]); },
'7003' : function(r) { return document.getElementById('circStrings').getFormattedString('staff.circ.renew.barcode', [params.barcode]); },
'7004' : function(r) {
removeCSSClass(document.documentElement,'PATRON_HAS_BILLS');
removeCSSClass(document.documentElement,'PATRON_HAS_OVERDUES');
removeCSSClass(document.documentElement,'PATRON_HAS_NOTES');
+ removeCSSClass(document.documentElement,'PATRON_HAS_LOST');
+ removeCSSClass(document.documentElement,'PATRON_HAS_LOST_AND_COUNTED');
removeCSSClass(document.documentElement,'PATRON_EXCEEDS_CHECKOUT_COUNT');
removeCSSClass(document.documentElement,'PATRON_EXCEEDS_OVERDUE_COUNT');
+ removeCSSClass(document.documentElement,'PATRON_EXCEEDS_LOST_COUNT');
removeCSSClass(document.documentElement,'PATRON_EXCEEDS_FINES');
removeCSSClass(document.documentElement,'NO_PENALTIES');
removeCSSClass(document.documentElement,'ONE_PENALTY');
<label class="hideme max_out_indicator" value="&staff.patron.display_overlay.max_checked_out.value;" command="cmd_patron_items"/>
<label class="hideme bills_indicator" value="&staff.patron.display_overlay.has_bills.value;" command="cmd_patron_bills"/>
<label class="hideme overdues_indicator" value="&staff.patron.display_overlay.has_overdues.value;" command="cmd_patron_items"/>
+ <label class="hideme max_lost_indicator" value="&staff.patron.display_overlay.max_lost.value;" command="cmd_patron_items"/>
+ <label class="hideme lost_indicator" value="&staff.patron.display_overlay.has_lost.value;" command="cmd_patron_items"/>
<label class="hideme invalid_dob_indicator" value="&staff.patron.display_overlay.invalid_dob.value;" command="cmd_patron_edit"/>
<label class="hideme invalid_address_indicator" value="&staff.patron.display_overlay.invalid_address.value;" command="cmd_patron_edit"/>
<label class="hideme invalid_email_indicator" value="&staff.patron.display_overlay.invalid_email.value;" command="cmd_patron_edit"/>
<label class="hideme max_out_indicator" value="&staff.patron.display_overlay.max_checked_out.value;" command="cmd_patron_items"/>
<label class="hideme bills_indicator" value="&staff.patron.display_overlay.has_bills.value;" command="cmd_patron_bills"/>
<label class="hideme overdues_indicator" value="&staff.patron.display_overlay.has_overdues.value;" command="cmd_patron_items"/>
+ <label class="hideme max_lost_indicator" value="&staff.patron.display_overlay.max_lost.value;" command="cmd_patron_items"/>
+ <label class="hideme lost_indicator" value="&staff.patron.display_overlay.has_lost.value;" command="cmd_patron_items"/>
<label class="hideme invalid_dob_indicator" value="&staff.patron.display_overlay.invalid_dob.value;" command="cmd_patron_edit"/>
<label class="hideme invalid_address_indicator" value="&staff.patron.display_overlay.invalid_address.value;" command="cmd_patron_edit"/>
<label class="hideme invalid_email_indicator" value="&staff.patron.display_overlay.invalid_email.value;" command="cmd_patron_edit"/>
try {
var robj = req.getResultObject();
var do_not_tally_claims_returned = String( obj.OpenILS.data.hash.aous['circ.do_not_tally_claims_returned'] ) == 'true';
+ var do_tally_lost = String( obj.OpenILS.data.hash.aous['circ.tally_lost'] ) == 'true';
util.widgets.set_text(e,
robj.out
+ robj.overdue
+ (do_not_tally_claims_returned ? 0 : robj.claims_returned)
+ robj.long_overdue
+ + (do_tally_lost ? robj.lost : 0)
);
if (e2) util.widgets.set_text(e2, robj.overdue );
if (e3) util.widgets.set_text(e3, robj.claims_returned );
+ robj.overdue
+ (do_not_tally_claims_returned ? 0 : robj.claims_returned)
+ robj.long_overdue
+ + (do_tally_lost ? robj.lost : 0)
)
/* + ( robj.overdue > 0 ? '*' : '' ) */
);
removeCSSClass(document.documentElement,'PATRON_HAS_BILLS');
removeCSSClass(document.documentElement,'PATRON_HAS_OVERDUES');
removeCSSClass(document.documentElement,'PATRON_HAS_NOTES');
+ removeCSSClass(document.documentElement,'PATRON_HAS_LOST');
+ removeCSSClass(document.documentElement,'PATRON_HAS_LOST_AND_COUNTED');
removeCSSClass(document.documentElement,'PATRON_EXCEEDS_CHECKOUT_COUNT');
removeCSSClass(document.documentElement,'PATRON_EXCEEDS_OVERDUE_COUNT');
+ removeCSSClass(document.documentElement,'PATRON_EXCEEDS_LOST_COUNT');
removeCSSClass(document.documentElement,'PATRON_EXCEEDS_FINES');
removeCSSClass(document.documentElement,'NO_PENALTIES');
removeCSSClass(document.documentElement,'ONE_PENALTY');
var notes = req.getResultObject();
if (notes.length > 0) addCSSClass(document.documentElement,'PATRON_HAS_NOTES');
});
+ net.simple_request('FM_CIRC_COUNT_RETRIEVE_VIA_USER.authoritative',[ ses(), patron.id() ], function(req) {
+ try {
+ var co = req.getResultObject();
+ if (co.lost > 0) addCSSClass(document.documentElement,'PATRON_HAS_LOST');
+ JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.init({'via':'stash'});
+ if ((String( data.hash.aous['circ.tally_lost'] ) == 'true') && (co.lost > 0)) addCSSClass(document.documentElement,'PATRON_HAS_LOST_AND_COUNTED');
+ } catch(E) {
+ alert(E);
+ }
+ });
/*
JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.init({'via':'stash'});
.PATRON_HAS_OVERDUES label.overdues_indicator { display: inline; color: #AA4400; }
.PATRON_HAS_OVERDUES label#under_items { color: red; }
+.PATRON_HAS_LOST .patronNameLarge { border-color: #FFC266; }
+.PATRON_HAS_LOST label.items_lost { color: #AA4400; }
+.PATRON_HAS_LOST label.items_lost.value { }
+.PATRON_HAS_LOST label.lost_indicator { display: inline; color: #AA4400; }
+
+.PATRON_HAS_LOST_AND_COUNTED label#under_items { color: red; }
+
.PATRON_EXCEEDS_CHECKOUT_COUNT .patronNameLarge { border-color: #C99DFF; }
.PATRON_EXCEEDS_CHECKOUT_COUNT label.items_out { color: purple; }
/* .PATRON_EXCEEDS_CHECKOUT_COUNT label.items_out.label { text-decoration: underline; } */
.PATRON_EXCEEDS_OVERDUE_COUNT label.items_overdue.value { }
.PATRON_EXCEEDS_OVERDUE_COUNT label.max_overdues_indicator { display: inline; color: purple; }
+.PATRON_EXCEEDS_LOST_COUNT .patronNameLarge { border-color: #C99DFF; }
+.PATRON_EXCEEDS_LOST_COUNT label.items_lost { color: purple; }
+.PATRON_EXCEEDS_LOST_COUNT label.items_lost.value { }
+.PATRON_EXCEEDS_LOST_COUNT label.max_lost_indicator { display: inline; color: purple; }
+
.PATRON_EXCEEDS_FINES .patronNameLarge { border-color: #C99DFF; }
.PATRON_EXCEEDS_FINES label.bill { color: purple; }
/* .PATRON_EXCEEDS_FINES label.bill.label { text-decoration: underline; } */
--- /dev/null
+New feature: "Patron blocking by lost items and include lost as items out."
+==========================================================================
+This feature has two main parts, both of which are to improve the staff's
+ability to assist patrons in regards to lost items.
+
+* Patron blocking by lost items. This will add a group penalty threshold
+that will alert staff when a patron has too many lost items. This
+setting is modified through the Group Penalty Thresholds page.
+
+* Include lost items as items out. Through a new library setting,
+'Include Lost circulations in lump sum tallies in Patron Display',
+the staff have the ability to determine if lost items will be included
+in items out.
+