From 79a52a9cee2b1d549ce59542c2edd6cd27acce31 Mon Sep 17 00:00:00 2001 From: miker Date: Wed, 20 Oct 2010 13:31:47 +0000 Subject: [PATCH] Backporting r18396 from trunk: Patch from Thomas Berezansky providing an alternate implementation of Hard Due Dates git-svn-id: svn://svn.open-ils.org/ILS/branches/rel_1_6@18398 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- Open-ILS/examples/fm_IDL.xml | 54 ++++++++++++++++------ Open-ILS/src/Makefile.am | 4 ++ .../src/perlmods/OpenILS/Application/AppUtils.pm | 11 +++++ .../perlmods/OpenILS/Application/Circ/Circulate.pm | 48 +++++++++++++++---- Open-ILS/src/sql/Pg/002.schema.config.sql | 47 ++++++++++++++++--- Open-ILS/src/sql/Pg/100.circ_matrix.sql | 1 + .../0442.schema.due_date_ceiling_alt_impl.sql | 53 +++++++++++++++++++++ .../support-scripts/update_hard_due_dates.srfsh | 7 +++ 8 files changed, 195 insertions(+), 30 deletions(-) create mode 100644 Open-ILS/src/sql/Pg/upgrade/0442.schema.due_date_ceiling_alt_impl.sql create mode 100755 Open-ILS/src/support-scripts/update_hard_due_dates.srfsh diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml index a424b78d6e..4c372d43b2 100644 --- a/Open-ILS/examples/fm_IDL.xml +++ b/Open-ILS/examples/fm_IDL.xml @@ -902,6 +902,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + @@ -914,6 +915,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + @@ -1562,7 +1564,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - @@ -1576,19 +1577,46 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + diff --git a/Open-ILS/src/Makefile.am b/Open-ILS/src/Makefile.am index df390b82b4..55c0249726 100644 --- a/Open-ILS/src/Makefile.am +++ b/Open-ILS/src/Makefile.am @@ -77,6 +77,8 @@ core_scripts = $(examples)/oils_ctl.sh \ $(supportscr)/fine_generator.pl \ $(supportscr)/hold_targeter.pl \ $(supportscr)/reshelving_complete.srfsh \ + $(supportscr)/clear_expired_circ_history.srfsh \ + $(supportscr)/update_hard_due_dates.srfsh \ $(supportscr)/juv_to_adult.srfsh \ $(supportscr)/thaw_expired_frozen_holds.srfsh \ $(supportscr)/long-overdue-status-update.pl \ @@ -229,6 +231,8 @@ ilscore-install: sed -i 's|LOCALSTATEDIR|@localstatedir@|g' '$(DESTDIR)@bindir@/autogen.sh' sed -i 's|SYSCONFDIR|@sysconfdir@|g' '$(DESTDIR)@bindir@/autogen.sh' sed -i 's|BINDIR|@bindir@|g' '$(DESTDIR)@bindir@/reshelving_complete.srfsh' + sed -i 's|BINDIR|@bindir@|g' '$(DESTDIR)@bindir@/clear_expired_circ_history.srfsh' + sed -i 's|BINDIR|@bindir@|g' '$(DESTDIR)@bindir@/update_hard_due_dates.srfsh' sed -i 's|BINDIR|@bindir@|g' '$(DESTDIR)@bindir@/juv_to_adult.srfsh' sed -i 's|BINDIR|@bindir@|g' '$(DESTDIR)@bindir@/long-overdue-status-update.pl' sed -i 's|SYSCONFDIR|@sysconfdir@|g' '$(DESTDIR)@bindir@/long-overdue-status-update.pl' diff --git a/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm b/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm index eb77f24c16..9b2774ebe9 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/AppUtils.pm @@ -903,6 +903,17 @@ sub fetch_max_fine_by_name { return ($obj, $evt); } +sub fetch_hard_due_date_by_name { + my( $self, $name ) = @_; + my( $obj, $evt ); + $obj = $self->simplereq( + 'open-ils.cstore', + 'open-ils.cstore.direct.config.hard_due_date.search.atomic', { name => $name } ); + $obj = $obj->[0]; + $evt = OpenILS::Event->new('CONFIG_RULES_HARD_DUE_DATE_NOT_FOUND') unless $obj; + return ($obj, $evt); +} + sub storagereq { my( $self, $method, @params ) = @_; return $self->simplereq( diff --git a/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm b/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm index 25285e3b87..450331b518 100644 --- a/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm +++ b/Open-ILS/src/perlmods/OpenILS/Application/Circ/Circulate.pm @@ -467,6 +467,7 @@ my @AUTOLOAD_FIELDS = qw/ recurring_fines_rule max_fine_rule renewal_remaining + hard_due_date due_date fulfilled_holds transit @@ -1075,7 +1076,7 @@ sub run_indb_circ_test { $mp, { flesh => 1, flesh_fields => {ccmm => - ['duration_rule', 'recurring_fine_rule', 'max_fine_rule']} + ['duration_rule', 'recurring_fine_rule', 'max_fine_rule', 'hard_due_date']} } ]) ); @@ -1113,9 +1114,10 @@ sub do_inspect { my $duration_rule = $self->circ_matrix_matchpoint->duration_rule; my $recurring_fine_rule = $self->circ_matrix_matchpoint->recurring_fine_rule; my $max_fine_rule = $self->circ_matrix_matchpoint->max_fine_rule; + my $hard_due_date = $self->circ_matrix_matchpoint->hard_due_date; my $policy = $self->get_circ_policy( - $duration_rule, $recurring_fine_rule, $max_fine_rule); + $duration_rule, $recurring_fine_rule, $max_fine_rule, $hard_due_date); $$results{$_} = $$policy{$_} for keys %$policy; } @@ -1128,7 +1130,7 @@ sub do_inspect { # fine based on the current copy # --------------------------------------------------------------------- sub get_circ_policy { - my($self, $duration_rule, $recurring_fine_rule, $max_fine_rule) = @_; + my($self, $duration_rule, $recurring_fine_rule, $max_fine_rule, $hard_due_date) = @_; my $policy = { duration_rule => $duration_rule->name, @@ -1136,10 +1138,18 @@ sub get_circ_policy { max_fine_rule => $max_fine_rule->name, max_fine => $self->get_max_fine_amount($max_fine_rule), fine_interval => $recurring_fine_rule->recurance_interval, - renewal_remaining => $duration_rule->max_renewals, - duration_date_ceiling => $duration_rule->date_ceiling + renewal_remaining => $duration_rule->max_renewals }; + if($hard_due_date) { + $policy->{duration_date_ceiling} = $hard_due_date->ceiling_date; + $policy->{duration_date_ceiling_force} = $hard_due_date->forceto; + } + else { + $policy->{duration_date_ceiling} = undef; + $policy->{duration_date_ceiling_force} = undef; + } + $policy->{duration} = $duration_rule->shrt if $self->copy->loan_duration == OILS_CIRC_DURATION_SHORT; $policy->{duration} = $duration_rule->normal @@ -1560,15 +1570,18 @@ sub run_checkout_scripts { my $duration; my $recurring; my $max_fine; + my $hard_due_date; my $duration_name; my $recurring_name; my $max_fine_name; + my $hard_due_date_name; if(!$self->legacy_script_support) { $self->run_indb_circ_test(); $duration = $self->circ_matrix_matchpoint->duration_rule; $recurring = $self->circ_matrix_matchpoint->recurring_fine_rule; $max_fine = $self->circ_matrix_matchpoint->max_fine_rule; + $hard_due_date = $self->circ_matrix_matchpoint->hard_due_date; } else { @@ -1580,6 +1593,7 @@ sub run_checkout_scripts { $duration_name = $result->{durationRule}; $recurring_name = $result->{recurringFinesRule}; $max_fine_name = $result->{maxFine}; + $hard_due_date_name = $result->{hardDueDate}; } $duration_name = $duration->name if $duration; @@ -1594,6 +1608,11 @@ sub run_checkout_scripts { ($max_fine, $evt) = $U->fetch_max_fine_by_name($max_fine_name); return $self->bail_on_events($evt) if ($evt && !$nobail); + + if($hard_due_date_name) { + ($hard_due_date, $evt) = $U->fetch_hard_due_date_by_name($hard_due_date_name); + return $self->bail_on_events($evt) if ($evt && !$nobail); + } } } else { @@ -1602,11 +1621,13 @@ sub run_checkout_scripts { $duration = undef; $recurring = undef; $max_fine = undef; + $hard_due_date = undef; } $self->duration_rule($duration); $self->recurring_fines_rule($recurring); $self->max_fine_rule($max_fine); + $self->hard_due_date($hard_due_date); } @@ -1617,21 +1638,28 @@ sub build_checkout_circ_object { my $duration = $self->duration_rule; my $max = $self->max_fine_rule; my $recurring = $self->recurring_fines_rule; + my $hard_due_date = $self->hard_due_date; my $copy = $self->copy; my $patron = $self->patron; my $duration_date_ceiling; + my $duration_date_ceiling_force; if( $duration ) { - my $policy = $self->get_circ_policy($duration, $recurring, $max); + my $policy = $self->get_circ_policy($duration, $recurring, $max, $hard_due_date); $duration_date_ceiling = $policy->{duration_date_ceiling}; + $duration_date_ceiling_force = $policy->{duration_date_ceiling_force}; my $dname = $duration->name; my $mname = $max->name; my $rname = $recurring->name; + my $hdname; + if($hard_due_date) { + $hdname = $hard_due_date->name; + } $logger->debug("circulator: building circulation ". - "with duration=$dname, maxfine=$mname, recurring=$rname"); + "with duration=$dname, maxfine=$mname, recurring=$rname, hard due date=$hdname"); $circ->duration($policy->{duration}); $circ->recuring_fine($policy->{recurring_fine}); @@ -1672,7 +1700,7 @@ sub build_checkout_circ_object { # if a patron is renewing, 'requestor' will be the patron $circ->circ_staff($self->editor->requestor->id); - $circ->due_date( $self->create_due_date($circ->duration, $duration_date_ceiling) ) if $circ->duration; + $circ->due_date( $self->create_due_date($circ->duration, $duration_date_ceiling, $duration_date_ceiling_force) ) if $circ->duration; $self->circ($circ); } @@ -1872,7 +1900,7 @@ sub apply_modified_due_date { sub create_due_date { - my( $self, $duration, $date_ceiling ) = @_; + my( $self, $duration, $date_ceiling, $force_date ) = @_; # if there is a raw time component (e.g. from postgres), # turn it into an interval that interval_to_seconds can parse @@ -1886,7 +1914,7 @@ sub create_due_date { if($date_ceiling) { my $cdate = DateTime::Format::ISO8601->new->parse_datetime(cleanse_ISO8601($date_ceiling)); - if ($cdate > DateTime->now and $cdate < $due_date) { + if ($cdate > DateTime->now and ($cdate < $due_date or $force_date)) { $logger->info("circulator: overriding due date with date ceiling: $date_ceiling"); $due_date = $cdate; } diff --git a/Open-ILS/src/sql/Pg/002.schema.config.sql b/Open-ILS/src/sql/Pg/002.schema.config.sql index f2d8a05377..e942ebc307 100644 --- a/Open-ILS/src/sql/Pg/002.schema.config.sql +++ b/Open-ILS/src/sql/Pg/002.schema.config.sql @@ -250,8 +250,7 @@ CREATE TABLE config.rule_circ_duration ( extended INTERVAL NOT NULL, normal INTERVAL NOT NULL, shrt INTERVAL NOT NULL, - max_renewals INT NOT NULL, - date_ceiling TIMESTAMPTZ + max_renewals INT NOT NULL ); COMMENT ON TABLE config.rule_circ_duration IS $$ /* @@ -278,13 +277,47 @@ COMMENT ON TABLE config.rule_circ_duration IS $$ $$; CREATE TABLE config.hard_due_date ( - id SERIAL PRIMARY KEY, - duration_rule INT NOT NULL REFERENCES config.rule_circ_duration (id) - DEFERRABLE INITIALLY DEFERRED, - ceiling_date TIMESTAMPTZ NOT NULL, - active_date TIMESTAMPTZ NOT NULL + id SERIAL PRIMARY KEY, + name TEXT NOT NULL UNIQUE CHECK ( name ~ E'^\\w+$' ), + ceiling_date TIMESTAMPTZ NOT NULL, + forceto BOOL NOT NULL, + owner INT NOT NULL -- REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED, ); +CREATE TABLE config.hard_due_date_values ( + id SERIAL PRIMARY KEY, + hard_due_date INT NOT NULL REFERENCES config.hard_due_date (id) + DEFERRABLE INITIALLY DEFERRED, + ceiling_date TIMESTAMPTZ NOT NULL, + active_date TIMESTAMPTZ NOT NULL +); + + +CREATE OR REPLACE FUNCTION config.update_hard_due_dates () RETURNS INT AS $func$ +DECLARE + temp_value config.hard_due_date_values%ROWTYPE; + updated INT := 0; +BEGIN + FOR temp_value IN + SELECT DISTINCT ON (hard_due_date) * + FROM config.hard_due_date_values + WHERE active_date <= NOW() -- We've passed (or are at) the rollover time + ORDER BY active_date DESC -- Latest (nearest to us) active time + LOOP + UPDATE config.hard_due_date + SET ceiling_date = temp_value.ceiling_date + WHERE id = temp_value.hard_due_date + AND ceiling_date <> temp_value.ceiling_date; -- Time is equal if we've already updated the chdd + + IF FOUND THEN + updated := updated + 1; + END IF; + END LOOP; + + RETURN updated; +END; +$func$ LANGUAGE plpgsql; + CREATE TABLE config.rule_max_fine ( id SERIAL PRIMARY KEY, name TEXT NOT NULL UNIQUE CHECK ( name ~ E'^\\w+$' ), diff --git a/Open-ILS/src/sql/Pg/100.circ_matrix.sql b/Open-ILS/src/sql/Pg/100.circ_matrix.sql index 34909c47f8..5811f1e15a 100644 --- a/Open-ILS/src/sql/Pg/100.circ_matrix.sql +++ b/Open-ILS/src/sql/Pg/100.circ_matrix.sql @@ -113,6 +113,7 @@ CREATE TABLE config.circ_matrix_matchpoint ( duration_rule INT NOT NULL REFERENCES config.rule_circ_duration (id) DEFERRABLE INITIALLY DEFERRED, recurring_fine_rule INT NOT NULL REFERENCES config.rule_recuring_fine (id) DEFERRABLE INITIALLY DEFERRED, max_fine_rule INT NOT NULL REFERENCES config.rule_max_fine (id) DEFERRABLE INITIALLY DEFERRED, + hard_due_date INT REFERENCES config.hard_due_date (id) DEFERRABLE INITIALLY DEFERRED, script_test TEXT, -- javascript source CONSTRAINT ep_once_per_grp_loc_mod_marc UNIQUE (grp, org_unit, circ_modifier, marc_type, marc_form, marc_vr_format, ref_flag, juvenile_flag, usr_age_lower_bound, usr_age_upper_bound, is_renewal) ); diff --git a/Open-ILS/src/sql/Pg/upgrade/0442.schema.due_date_ceiling_alt_impl.sql b/Open-ILS/src/sql/Pg/upgrade/0442.schema.due_date_ceiling_alt_impl.sql new file mode 100644 index 0000000000..4e01f02a65 --- /dev/null +++ b/Open-ILS/src/sql/Pg/upgrade/0442.schema.due_date_ceiling_alt_impl.sql @@ -0,0 +1,53 @@ +BEGIN; + +INSERT INTO config.upgrade_log (version) VALUES ('0442'); -- tsbere via miker + +DROP TABLE config.hard_due_date; + +CREATE TABLE config.hard_due_date ( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL UNIQUE CHECK ( name ~ E'^\\w+$' ), + ceiling_date TIMESTAMPTZ NOT NULL, + forceto BOOL NOT NULL, + owner INT NOT NULL +); + +CREATE TABLE config.hard_due_date_values ( + id SERIAL PRIMARY KEY, + hard_due_date INT NOT NULL REFERENCES config.hard_due_date (id) + DEFERRABLE INITIALLY DEFERRED, + ceiling_date TIMESTAMPTZ NOT NULL, + active_date TIMESTAMPTZ NOT NULL +); + +ALTER TABLE config.circ_matrix_matchpoint ADD COLUMN hard_due_date INT REFERENCES config.hard_due_date (id); + +ALTER TABLE config.rule_circ_duration DROP COLUMN date_ceiling; + +CREATE OR REPLACE FUNCTION config.update_hard_due_dates () RETURNS INT AS $func$ +DECLARE + temp_value config.hard_due_date_values%ROWTYPE; + updated INT := 0; +BEGIN + FOR temp_value IN + SELECT DISTINCT ON (hard_due_date) * + FROM config.hard_due_date_values + WHERE active_date <= NOW() -- We've passed (or are at) the rollover time + ORDER BY active_date DESC -- Latest (nearest to us) active time + LOOP + UPDATE config.hard_due_date + SET ceiling_date = temp_value.ceiling_date + WHERE id = temp_value.hard_due_date + AND ceiling_date <> temp_value.ceiling_date; -- Time is equal if we've already updated the chdd + + IF FOUND THEN + updated := updated + 1; + END IF; + END LOOP; + + RETURN updated; +END; +$func$ LANGUAGE plpgsql; + +COMMIT; + diff --git a/Open-ILS/src/support-scripts/update_hard_due_dates.srfsh b/Open-ILS/src/support-scripts/update_hard_due_dates.srfsh new file mode 100755 index 0000000000..f14a24ce53 --- /dev/null +++ b/Open-ILS/src/support-scripts/update_hard_due_dates.srfsh @@ -0,0 +1,7 @@ +#!/openils/bin/srfsh +open open-ils.cstore +request open-ils.cstore open-ils.cstore.transaction.begin +request open-ils.cstore open-ils.cstore.json_query {"from":["config.update_hard_due_dates"]} +request open-ils.cstore open-ils.cstore.transaction.commit +close open-ils.cstore + -- 2.11.0