Bugzilla – Attachment 41147 Details for
Bug 11023
Automatic item modification by age (Was "Toggle new status for items")
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 11023: Automatic item modification by age (Was Toggle "new" status")
Bug-11023-Automatic-item-modification-by-age-Was-T.patch (text/plain), 41.16 KB, created by
Julian Maurice
on 2015-07-24 10:06:41 UTC
(
hide
)
Description:
Bug 11023: Automatic item modification by age (Was Toggle "new" status")
Filename:
MIME Type:
Creator:
Julian Maurice
Created:
2015-07-24 10:06:41 UTC
Size:
41.16 KB
patch
obsolete
>From d54934fbb0dc15e720233c9de43fe832b65b6cea Mon Sep 17 00:00:00 2001 >From: Jonathan Druart <jonathan.druart@biblibre.com> >Date: Wed, 25 Sep 2013 16:45:14 +0200 >Subject: [PATCH] Bug 11023: Automatic item modification by age (Was Toggle > "new" status") > >This patch adds: >- a new DB field items.new. >- a new page to configure this new status > (tools/toggle_new_status.pl). >- a new cronjob script (misc/cronjobs/automatic_item_modification_by_age.pl >was misc/cronjob/toggle_new_status.pl) > >Why this status is useful for some libraries ? >The use cases are: >- to know easily what are the new items (with a simple sql query). >- to display an icon in the search results. >- issuing rules can be adapt for new items. Automatically (using the > cronjob script), the status change (depending the configuration) and > the item can be issued, for example. >- a RSS/Atom feeds can be created on these new items. > >Test plan: >- log in with a librarian having the tools > items_batchmod permission. >- navigate to Home > Tools > Automatic item modifications by age (was: Toggle new status) >- click on the edit button >- there are 3 "blocks": > * duration: the duration during an item is considered as new. > * conditions: the status will change only if the conditions are meet. > * substitutions: if there is no substitution, no action will be done. > You can add some change to apply to the matching items. > E.g. ccode=3 > new='' > If the value is an empty string (in other words, the input does not > contain anything), the field will be deleted. > You can create as many rules as you want. >- test the interface : add/remove rule, conditions, substitutions, > submit the form, edit, etc. > (There is a looot of JS everywhere, so certainly a looot of bugs...). >- when you have your rules defined, you can now launch the cronjob > script without any parameter. > A report will be displayed with the matching itemnumber and the > substitutions to apply. Verify results are consistent. >- launch the script with the -c argument and verify values have been > modified depending the substitution rules. > >Signed-off-by: juliette et remy <juliette.levast@iepg.fr> > >Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com> > >Bug 11023: Add the ability to specify fields from biblioitems table. > >Test plan: >Same as before but try with fields from the biblioitems table. > >Signed-off-by: juliette et remy <juliette.levast@iepg.fr> > >Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com> > >Bug 11023: Add UT for C4::Items::ToggleNewStatus > >Test plan: >prove t/db_dependent/Items/ToggleNewStatus.t > >Signed-off-by: juliette et remy <juliette.levast@iepg.fr> > >Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com> > >Bug 11023: FIX - condition on biblioitems table does not work > >If a rule contains a condition on the biblioitems table, the match won't >work. This patch fixes this issue. > >Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com> > >Bug 11023: Use DBIx-Class to retrieve column names > >Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com> > >Bug 11023: Don't use the biblioitems fields for the subtitution > >It's dangerous to allow a change on the biblioitems fields with this >feature. > >Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com> > >Bug 11023: Rename the duration parameter with 'age' > >Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com> > >Bug 11023: The age parameter should be a number > >The template should check if the age parameter is correctly filled >(should be a number). > >Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com> > >Bug 11023: Change the name of the feature > >Originaly this feature only permits to update the "new" field. >Now all item fields can be updated. >The name of the feature is now "Automatic item modifications by age". > >Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com> > >Bug 11023 [QA Followup] > >* Update DB version >* Fix capitalization error >* Rename misc/cronjobs/toggle_new_status.pl to misc/cronjobs/automatic_item_modification_by_age.pl > >Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com> > >Bug 11023 [QA Followup] - Complete the renaming of "toggle new status" to "automatic item modification by age" > >Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com> >--- > C4/Items.pm | 96 ++++++- > .../intranet-tmpl/prog/en/css/staff-global.css | 30 +++ > .../tools/automatic_item_modification_by_age.tt | 45 ++++ > .../tools/automatic_item_modification_by_age.tt | 294 +++++++++++++++++++++ > .../prog/en/modules/tools/tools-home.tt | 5 + > .../cronjobs/automatic_item_modification_by_age.pl | 106 ++++++++ > .../Items/AutomaticItemModificationByAge.t | 283 ++++++++++++++++++++ > tools/automatic_item_modification_by_age.pl | 119 +++++++++ > 8 files changed, 977 insertions(+), 1 deletion(-) > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/help/tools/automatic_item_modification_by_age.tt > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/tools/automatic_item_modification_by_age.tt > create mode 100755 misc/cronjobs/automatic_item_modification_by_age.pl > create mode 100644 t/db_dependent/Items/AutomaticItemModificationByAge.t > create mode 100755 tools/automatic_item_modification_by_age.pl > >diff --git a/C4/Items.pm b/C4/Items.pm >index 55bb4bd..0c5fdc3 100644 >--- a/C4/Items.pm >+++ b/C4/Items.pm >@@ -35,6 +35,7 @@ use DateTime::Format::MySQL; > use Data::Dumper; # used as part of logging item record changes, not just for > # debugging; so please don't remove this > use Koha::DateUtils qw/dt_from_string/; >+use Koha::Database; > > use Koha::Database; > >@@ -473,6 +474,7 @@ sub _build_default_values_for_mod_marc { > location => undef, > permanent_location => undef, > materials => undef, >+ new => undef, > notforloan => 0, > # paidfor => undef, # commented, see bug 12817 > price => undef, >@@ -2184,7 +2186,8 @@ sub _koha_new_item { > enumchron = ?, > more_subfields_xml = ?, > copynumber = ?, >- stocknumber = ? >+ stocknumber = ?, >+ new = ? > "; > my $sth = $dbh->prepare($query); > my $today = C4::Dates->today('iso'); >@@ -2228,6 +2231,7 @@ sub _koha_new_item { > $item->{'more_subfields_xml'}, > $item->{'copynumber'}, > $item->{'stocknumber'}, >+ $item->{'new'}, > ); > > my $itemnumber; >@@ -3102,4 +3106,94 @@ sub PrepareItemrecordDisplay { > }; > } > >+=head2 columns >+ >+ my @columns = C4::Items::columns(); >+ >+Returns an array of items' table columns on success, >+and an empty array on failure. >+ >+=cut >+ >+sub columns { >+ my $rs = Koha::Database->new->schema->resultset('Item'); >+ return $rs->result_source->columns; >+} >+ >+=head2 biblioitems_columns >+ >+ my @columns = C4::Items::biblioitems_columns(); >+ >+Returns an array of biblioitems' table columns on success, >+and an empty array on failure. >+ >+=cut >+ >+sub biblioitems_columns { >+ my $rs = Koha::Database->new->schema->resultset('Biblioitem'); >+ return $rs->result_source->columns; >+} >+ >+sub ToggleNewStatus { >+ my ( $params ) = @_; >+ my @rules = @{ $params->{rules} }; >+ my $report_only = $params->{report_only}; >+ >+ my $dbh = C4::Context->dbh; >+ my @errors; >+ my @item_columns = map { "items.$_" } C4::Items::columns; >+ my @biblioitem_columns = map { "biblioitems.$_" } C4::Items::biblioitems_columns; >+ my $report; >+ for my $rule ( @rules ) { >+ my $age = $rule->{age}; >+ my $conditions = $rule->{conditions}; >+ my $substitutions = $rule->{substitutions}; >+ my @params; >+ >+ my $query = q| >+ SELECT items.biblionumber, items.itemnumber >+ FROM items >+ LEFT JOIN biblioitems ON biblioitems.biblionumber = items.biblionumber >+ WHERE 1 >+ |; >+ for my $condition ( @$conditions ) { >+ if ( >+ grep {/^$condition->{field}$/} @item_columns >+ or grep {/^$condition->{field}$/} @biblioitem_columns >+ ) { >+ if ( $condition->{value} =~ /\|/ ) { >+ my @values = split /\|/, $condition->{value}; >+ $query .= qq| AND $condition->{field} IN (| >+ . join( ',', ('?') x scalar @values ) >+ . q|)|; >+ push @params, @values; >+ } else { >+ $query .= qq| AND $condition->{field} = ?|; >+ push @params, $condition->{value}; >+ } >+ } >+ } >+ if ( defined $age ) { >+ $query .= q| AND TO_DAYS(NOW()) - TO_DAYS(dateaccessioned) >= ? |; >+ push @params, $age; >+ } >+ my $sth = $dbh->prepare($query); >+ $sth->execute( @params ); >+ while ( my $values = $sth->fetchrow_hashref ) { >+ my $biblionumber = $values->{biblionumber}; >+ my $itemnumber = $values->{itemnumber}; >+ my $item = C4::Items::GetItem( $itemnumber ); >+ for my $substitution ( @$substitutions ) { >+ next unless $substitution->{field}; >+ C4::Items::ModItem( {$substitution->{field} => $substitution->{value}}, $biblionumber, $itemnumber ) >+ unless $report_only; >+ push @{ $report->{$itemnumber} }, $substitution; >+ } >+ } >+ } >+ >+ return $report; >+} >+ >+ > 1; >diff --git a/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css b/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css >index 2c4011f..7fc7790 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css >+++ b/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css >@@ -2768,6 +2768,7 @@ span.onsite_checkout { > border-radius: 4px; > border : 1px solid #FFF2CE; > } >+ > div.lastchecked { > padding : .2em 1em; > border: 2px solid #BCDB89; >@@ -2798,3 +2799,32 @@ div#cn_browser_table_wrapper > table#cn_browser_table { > margin: auto; > width:90%; > } >+ >+/* Tools > automatic_item_modification_by_age */ >+div.rules { >+ display: block; >+} >+div#new_rule, div.rule { >+ background-color: #F4F8F9; >+ border: 2px solid #B9D8D9; >+ border-radius: 5px; >+ margin: .3em; >+ padding: .3em; >+} >+ >+div.duration, div.blocks { >+ border: 2px solid #B9D8D9; >+ border-radius: 5px 5px 5px 5px; >+ margin: .3em; >+ padding: 0 .3em .3em .3em; >+} >+ >+div.duration h5, div.blocks h5 { >+ padding-bottom: 4px; >+ padding-left: 0.2em; >+ background-color: #E6F0F2; >+ border-radius: 1px; >+} >+div.duration span, div.blocks div { >+ display:block; >+} >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/help/tools/automatic_item_modification_by_age.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/help/tools/automatic_item_modification_by_age.tt >new file mode 100644 >index 0000000..8fdda1b >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/help/tools/automatic_item_modification_by_age.tt >@@ -0,0 +1,45 @@ >+[% INCLUDE 'help-top.inc' %] >+<h1>Automatic item modifications by age configuration</h1> >+ >+<p>This configuration page allows to configure the rules for the automatic item modifications by age cronjob script.</p> >+ >+<p>Libraries can manage the 'new' status for items. With this script, it will be possible to:<p> >+<ul> >+ <li>know easily what are the new items in the catalogue.</li> >+ <li>display an icon in the search results for new items.</li> >+ <li>configure issuing rules depending the 'new' status.</li> >+ <li>get a RSS/Atom feeds on these new items.</li> >+</ul> >+ >+<h3>How to work the configuration page?</h3> >+<p>There are 3 values to define:</p> >+<h4>The duration</h4> >+<p>This value corresponds to the duration an item is considered as new.</p> >+<h4>The conditions</h4> >+<p>Conditions should be defined if you want to test some values before to substitute fields in the items.</p> >+<p>They are cumulatives but you can separate with a pipe '|' for a field with several values.</p> >+<h4>The substitutions</h4> >+<p>Substitutions are changes to apply to the matching items.</p> >+<p>At least one substitution must be defined, else there is no sense to launch the script.</p> >+<p>If the value is an empty string, the field will be deleted.</p> >+<h3>Examples</h3> >+<p>You want to remove the items.new value for items created 10 days ago:</p> >+<ul> >+ <li>Duration: 10 days</li> >+ <li>No condition</li> >+ <li>Substitution: items.new = '' (no value in the input)</li> >+</ul> >+ >+<p>You want to change the items.ccode=1 to items.ccode=2 for items created 7 days ago. >+<ul> >+ <li>Duration: 7 days</li> >+ <li>Condition: items.ccode = 1</li> >+ <li>Substitution: items.ccode = 2</li> >+</ul> >+ >+<h3>How to execute the cronjob script?</h3> >+<p>The cronjob script is misc/cronjobs/automatic_item_modification_by_age.pl.</p> >+<p>Try the -h parameter in order to see the help.</p> >+<p>Without any parameter, the script will be launched in a dry-run mode. If the -c (or --confirm) flag is given, the script will apply the changes.</p> >+ >+[% INCLUDE 'help-bottom.inc' %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/automatic_item_modification_by_age.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/automatic_item_modification_by_age.tt >new file mode 100644 >index 0000000..a08e96d >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/automatic_item_modification_by_age.tt >@@ -0,0 +1,294 @@ >+[% INCLUDE 'doc-head-open.inc' %] >+<title>Koha › Tools › Automatic item modifications by age</title> >+[% INCLUDE 'doc-head-close.inc' %] >+<script type="text/javascript">//<![CDATA[ >+ function clear_inputs(node, new_node) { >+ var selects = $(node).find("select"); >+ $(selects).each(function(i) { >+ var select = this; >+ $(new_node).find("select").eq(i).val($(select).val()); >+ }); >+ var inputs = $(node).find("input"); >+ $(inputs).each(function(i) { >+ var input = this; >+ $(new_node).find("input").eq(i).val($(input).val()); >+ }); >+ } >+ >+ function remove_block_action( link ) { >+ var blocks = $(link).parent().parent(); >+ if( $(blocks).find(".block").length > 2 ) { >+ $(blocks).find("a.remove_block").show(); >+ } else { >+ $(blocks).find("a.remove_block").hide(); >+ } >+ $(link).parent().remove(); >+ } >+ >+ function remove_rule_action( link ) { >+ if( $("#rules").find("div.rule").length < 2 ) { >+ $("#rules").hide(); >+ $("#norules").show(); >+ } >+ $(link).parent().remove(); >+ } >+ >+ function clone_block(block) { >+ var new_block = $(block).clone(1); >+ clear_inputs(block, new_block); >+ $(new_block).find('a.remove_block').show(); >+ var blocks = $(block).parent(); >+ $(blocks).append(new_block); >+ $(blocks).find('a.remove_block').click(function(){ >+ remove_block_action($(this)); >+ }).show(); >+ } >+ >+ $(document).ready(function() { >+ $("#new_rule a.remove_rule").hide(); >+ $("#new_rule a.remove_block").hide(); >+ $("#rules a.remove_block").click(function(){ >+ remove_block_action($(this)); >+ }); >+ $("#rules a.remove_rule").click(function(){ >+ remove_rule_action($(this)); >+ }); >+ >+ var unique_id = $("div.rule").length + 1; >+ $("a.add_rule").click(function(){ >+ var rule = $("#new_rule"); >+ var new_rule = $(rule).clone(1); >+ $(new_rule).removeAttr('id'); >+ $(new_rule).attr('class', 'rule'); >+ clear_inputs(rule, new_rule); >+ $(new_rule).find("select[name='condition_field']").attr('name', 'condition_field_' + unique_id); >+ $(new_rule).find("select[name='substitution_field']").attr('name', 'substitution_field_' + unique_id); >+ $(new_rule).find("input[name='condition_value']").attr('name', 'condition_value_' + unique_id); >+ $(new_rule).find("input[name='substitution_value']").attr('name', 'substitution_value_' + unique_id); >+ $(new_rule).find("input[name='age']").attr('name', 'age_' + unique_id); >+ $(new_rule).find("input[name='unique_id']").val(unique_id); >+ >+ $("#rules").append(new_rule); >+ >+ if( $("#rules").find("div.rule").length > 0 ) { >+ $("#rules").show(); >+ $("#norules").hide(); >+ } >+ if( $("#rules").find(".conditions > .condition").length > 1 ) { >+ >+ } >+ if( $("#rules").find(".conditions > .condition").length > 1 ) { >+ >+ } >+ $(new_rule).find('a.remove_rule').click(function(){ >+ remove_rule_action( $(this) ); >+ }).show(); >+ $(new_rule).find('a.add_rule').remove(); >+ unique_id++; >+ }); >+ >+ $("a.add_block").click(function(){ >+ clone_block( $(this).parent() ); >+ }); >+ >+ if( $("#rules").find("div.rule").length < 1 ) { >+ $("#rules").hide(); >+ $("#norules").show(); >+ } >+ >+ $("#rules .rule .blocks").each(function(){ >+ if ( $(this).find(".block").length == 1 ) { >+ $(this).find("a.remove_block").hide(); >+ } >+ }); >+ >+ [% IF op == 'edit_form' %] >+ [% IF rules.size > 0 %] >+ $("#norules").hide(); >+ [% ELSE %] >+ $("#rules").show(); >+ [% END %] >+ [% END %] >+ }); >+//]]> >+</script> >+</head> >+<body id="tools_automatic_item_modification_by_age" class="tools"> >+[% INCLUDE 'header.inc' %] >+[% INCLUDE 'cat-search.inc' %] >+<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> › <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a> › <a href="/cgi-bin/koha/tools/automatic_item_modification_by_age.pl">Automatic item modifications by age</a></div> >+ >+<div id="doc3" class="yui-t2"> >+ <div id="bd"> >+ <div id="yui-main"> >+ <div class="yui-b"> >+ <h3>Automatic item modifications by age</h3> >+ <div id="toolbar" class="btn-toolbar"> >+ <a class="btn btn-small" id="newentry" href="/cgi-bin/koha/tools/automatic_item_modification_by_age.pl?op=edit_form"><i class="icon-plus"></i> Edit</a> >+ </div> >+ [% FOR message IN messages %] >+ [% IF message.type == "error" %] >+ <div class="dialog error"> >+ [% END %] >+ [% IF message.code == "unable_to_load_configuration" %] >+ An error occurs: Unable to load the configuration. >+ [% END %] >+ </div> >+ [% END %] >+ >+ [% IF op == 'edit_form' %] >+ <form method="post" action="/cgi-bin/koha/tools/automatic_item_modification_by_age.pl"> >+ <div id="edit_rules"> >+ <h4>List of rules</h4> >+ <div id="rules"> >+ [% FOR rule IN rules %] >+ [% SET id = loop.count %] >+ <div class="rule"> >+ <input type="hidden" name="unique_id" value="[% loop.count %]" /> <!-- FIXME on update, the unique_id should be filled --> >+ <div class="age"> >+ <h5>Age</h5> >+ <input type="number" value="[% rule.age %]" name="age_[% id %]" /> days >+ </div> >+ <div class="blocks"> >+ <h5>Conditions</h5> >+ [% FOR condition IN rule.conditions %] >+ <div class="block"> >+ <select name="condition_field_[% id %]"> >+ <option value="">Choose a field name</option> >+ [% FOR field IN condition_fields %] >+ [% IF condition.field == field %] >+ <option value="[% field %]" selected="selected">[% field %]</option> >+ [% ELSE %] >+ <option value="[% field %]">[% field %]</option> >+ [% END %] >+ [% END %] >+ </select> >+ = >+ <input type="text" value="[% condition.value %]" name="condition_value_[% id%]" /> >+ <a class="add_block" style="cursor:pointer"><i class="icon-plus"></i></a> >+ <a class="remove_block" style="cursor:pointer"><i class="icon-remove"></i></a> >+ </div> >+ [% END %] >+ </div> >+ <div class="blocks"> >+ <h5>Substitutions</h5> >+ [% FOR substitution IN rule.substitutions %] >+ <div class="block"> >+ <select name="substitution_field_[% id %]"> >+ <option value="">Choose a field name</option> >+ [% FOR field IN substitution_fields %] >+ [% IF substitution.field == field %] >+ <option value="[% field %]" selected="selected">[% field %]</option> >+ [% ELSE %] >+ <option value="[% field %]">[% field %]</option> >+ [% END %] >+ [% END %] >+ </select> >+ = >+ <input type="text" value="[% substitution.value %]" name="substitution_value_[% id %]" /> >+ <a class="add_block" style="cursor:pointer"><i class="icon-plus"></i></a> >+ <a class="remove_block" style="cursor:pointer"><i class="icon-remove"></i></a> >+ </div> >+ [% END %] >+ </div> >+ <a class="remove_rule" style="cursor:pointer">Remove this rule</a> >+ </div> >+ [% END %] >+ </div> >+ <div id="norules"> >+ There is no rule defined. >+ </div> >+ <fieldset class="action"> >+ <input type="hidden" name="op" value="update" /> >+ <a class="cancel" href="/cgi-bin/koha/tools/automatic_item_modification_by_age.pl">Cancel</a> >+ <input type="submit" value="Submit theses rules" /> >+ </fieldset> >+ </div> >+ </form> >+ <h4>Add a new rule</h4> >+ <div id="new_rule"> >+ <input type="hidden" name="unique_id" /> >+ <div class="age"> >+ <h5>Age</h5> >+ <input type="number" value="" name="age" /> days >+ </div> >+ <div class="blocks"> >+ <h5>Conditions</h5> >+ <div class="block"> >+ <select name="condition_field"> >+ <option value="">Choose a field name</option> >+ [% FOR field IN condition_fields %] >+ <option value="[% field %]">[% field %]</option> >+ [% END %] >+ </select> >+ = >+ <input type="text" value="" name="condition_value" /> >+ <a class="add_block" style="cursor:pointer"><i class="icon-plus"></i></a> >+ <a class="remove_block" style="cursor:pointer"><i class="icon-remove"></i></a> >+ </div> >+ </div> >+ <div class="blocks"> >+ <h5>Substitutions</h5> >+ <div class="block"> >+ <select name="substitution_field"> >+ <option value="">Choose a field name</option> >+ [% FOR field IN substitution_fields %] >+ <option value="[% field %]">[% field %]</option> >+ [% END %] >+ </select> >+ = >+ <input type="text" value="" name="substitution_value" /> >+ <a class="add_block" style="cursor:pointer"><i class="icon-plus"></i></a> >+ <a class="remove_block" style="cursor:pointer"><i class="icon-remove"></i></a> >+ </div> >+ </div> >+ <a class="add_rule" style="cursor:pointer">Add this rule</a> >+ <a class="remove_rule" style="cursor:pointer">Remove this rule</a> >+ </div> >+ [% ELSIF rules and op == 'show' %] >+ <div id="rules"> >+ <h4>List of rules</h4> >+ [% FOR rule IN rules %] >+ <div class="rule"> >+ <div class="age"> >+ <h5>Age</h5> >+ [% IF rule.age.defined and rule.age.length > 0 %] >+ [% rule.age %] days >+ [% ELSE %] >+ There is no age for this rule. >+ [% END %] >+ </div> >+ <div class="blocks"> >+ <h5>Conditions</h5> >+ [% FOR condition IN rule.conditions %] >+ [% IF condition.field %] >+ <div class="block"> >+ [% condition.field %] = [% condition.value %] >+ </div> >+ [% ELSE %] >+ There is no condition for this rule. >+ [% END %] >+ [% END %] >+ </div> >+ <div class="blocks"> >+ <h5>Substitutions</h5> >+ [% FOR substitution IN rule.substitutions %] >+ <div class="block"> >+ [% substitution.field %] = [% substitution.value %] >+ </div> >+ [% END %] >+ </div> >+ </div> >+ [% END %] >+ </div> >+ [% ELSE %] >+ There is no rule defined. Please click on the edit button. >+ [% END %] >+ >+ </div> >+ </div> >+ <div class="yui-b noprint"> >+ [% INCLUDE 'tools-menu.inc' %] >+ </div> >+</div> >+[% INCLUDE 'intranet-bottom.inc' %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt >index 7b2e14e..9fc0214 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt >@@ -136,6 +136,11 @@ > <dd>Modify a batch of records (biblios or authorities)</dd> > [% END %] > >+ [% IF ( CAN_user_tools_items_batchmod ) %] >+ <dt><a href="/cgi-bin/koha/tools/automatic_item_modification_by_age.pl">Automatic item modifications by age</a></dt> >+ <dd>Define rules to modify items by age</dd> >+ [% END %] >+ > [% IF ( CAN_user_tools_export_catalog ) %] > <dt><a href="/cgi-bin/koha/tools/export.pl">Export data</a></dt> > <dd>Export bibliographic, holdings, and authority records</dd> >diff --git a/misc/cronjobs/automatic_item_modification_by_age.pl b/misc/cronjobs/automatic_item_modification_by_age.pl >new file mode 100755 >index 0000000..51effd3 >--- /dev/null >+++ b/misc/cronjobs/automatic_item_modification_by_age.pl >@@ -0,0 +1,106 @@ >+#!/usr/bin/perl >+ >+use Modern::Perl; >+ >+use Getopt::Long; >+use Pod::Usage; >+use JSON; >+ >+use C4::Context; >+use C4::Items; >+ >+# Getting options >+my ( $verbose, $help, $confirm ); >+my $result = GetOptions( >+ 'h|help' => \$help, >+ 'v|verbose' => \$verbose, >+ 'c|confirm' => \$confirm, >+); >+ >+pod2usage(1) if $help; >+$verbose = 1 unless $confirm; >+ >+# Load configuration from the syspref >+my $syspref_content = C4::Context->preference('automatic_item_modification_by_age_configuration'); >+my $rules = eval { JSON::from_json( $syspref_content ) }; >+pod2usage({ -message => "Unable to load the configuration : $@", -exitval => 1 }) >+ if $@; >+ >+my $report = C4::Items::ToggleNewStatus( { rules => $rules, report_only => not $confirm } ); >+ >+if ( $verbose ) { >+ if ( $report ) { >+ say "Item to modify:"; >+ while ( my ( $itemnumber, $substitutions ) = each %$report ) { >+ for my $substitution ( @$substitutions ) { >+ if ( defined $substitution->{value} and $substitution->{value} ne q|| ) { >+ say "\titemnumber $itemnumber: $substitution->{field}=$substitution->{value}"; >+ } else { >+ say "\titemnumber $itemnumber: field $substitution->{field} to delete"; >+ } >+ } >+ } >+ } else { >+ say "There is no item to modify"; >+ } >+} >+ >+exit(0); >+ >+__END__ >+ >+=head1 NAME >+ >+automatic_item_modification_by_age.pl >+ >+=head1 SYNOPSIS >+ >+./automatic_item_modification_by_age.pl -h >+ >+Toggle recent acquisitions status. >+Use this script to delete "new" status for items. >+ >+=head1 OPTIONS >+ >+=over 8 >+ >+=item B<-h|--help> >+ >+Prints this help message. >+ >+=item B<-v|--verbose> >+ >+Set the verbose flag. >+ >+=item B<-c|--confirm> >+ >+The script will modify the items. >+ >+=back >+ >+=head1 AUTHOR >+ >+Jonathan Druart <jonathan.druart@biblibre.com> >+ >+=head1 COPYRIGHT >+ >+Copyright 2013 BibLibre >+ >+=head1 LICENSE >+ >+This file is part of Koha. >+ >+Koha 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 3 of the License, or >+(at your option) any later version. >+ >+Koha 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. >+ >+You should have received a copy of the GNU General Public License >+along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+=cut >diff --git a/t/db_dependent/Items/AutomaticItemModificationByAge.t b/t/db_dependent/Items/AutomaticItemModificationByAge.t >new file mode 100644 >index 0000000..3f2943c >--- /dev/null >+++ b/t/db_dependent/Items/AutomaticItemModificationByAge.t >@@ -0,0 +1,283 @@ >+#!/usr/bin/perl >+ >+use Modern::Perl; >+use Test::More tests => 16; >+use MARC::Record; >+use MARC::Field; >+use DateTime; >+use DateTime::Duration; >+ >+use C4::Biblio; >+use C4::Context; >+use C4::Items; >+use Koha::DateUtils; >+ >+my $dbh = C4::Context->dbh; >+$dbh->{AutoCommit} = 0; >+$dbh->{RaiseError} = 1; >+ >+$dbh->do(q| >+ DELETE FROM marc_subfield_structure >+ WHERE kohafield = 'items.new' OR kohafield = 'items.stocknumber' >+|); >+ >+my $new_tagfield = 'i'; >+$dbh->do(qq| >+ INSERT INTO marc_subfield_structure(tagfield, tagsubfield, kohafield, frameworkcode) >+ VALUES ( 952, '$new_tagfield', 'items.new', '' ) >+|); >+ >+my $record = MARC::Record->new(); >+$record->append_fields( >+ MARC::Field->new('100', ' ', ' ', a => 'Moffat, Steven'), >+ MARC::Field->new('245', ' ', ' ', a => 'Silence in the library'), >+ MARC::Field->new('942', ' ', ' ', c => 'ITEMTYPE_T'), >+); >+my ($biblionumber, undef) = C4::Biblio::AddBiblio($record, ''); >+ >+my ($item_bibnum, $item_bibitemnum, $itemnumber) = C4::Items::AddItem( >+ { >+ homebranch => 'CPL', >+ holdingbranch => 'CPL', >+ new => 'new_value', >+ ccode => 'FIC', >+ }, >+ $biblionumber >+); >+ >+my $item = C4::Items::GetItem( $itemnumber ); >+is ( $item->{new}, 'new_value', q|AddItem insert the 'new' field| ); >+ >+my ( $tagfield, undef ) = GetMarcFromKohaField('items.itemnumber', ''); >+my $marc_item = C4::Items::GetMarcItem( $biblionumber, $itemnumber ); >+is( $marc_item->subfield($tagfield, $new_tagfield), 'new_value', q|Koha mapping is correct|); >+ >+# Update the items.new field if items.ccode eq 'FIC' => should be updated >+my @rules = ( >+ { >+ conditions => [ >+ { >+ field => 'items.ccode', >+ value => 'FIC', >+ }, >+ ], >+ substitutions => [ >+ { >+ field => 'items.new', >+ value => 'updated_value', >+ }, >+ ], >+ age => '0', >+ }, >+); >+ >+C4::Items::ToggleNewStatus( { rules => \@rules } ); >+ >+my $modified_item = C4::Items::GetItem( $itemnumber ); >+is( $modified_item->{new}, 'updated_value', q|ToggleNewStatus: The new value is updated|); >+$marc_item = C4::Items::GetMarcItem( $biblionumber, $itemnumber ); >+is( $marc_item->subfield($tagfield, $new_tagfield), 'updated_value', q|ToggleNewStatus: The new value is updated| ); >+ >+# Update the items.new field if items.ccode eq 'DONT_EXIST' => should not be updated >+@rules = ( >+ { >+ conditions => [ >+ { >+ field => 'items.ccode', >+ value => 'DONT_EXIST', >+ }, >+ ], >+ substitutions => [ >+ { >+ field => 'items.new', >+ value => 'new_updated_value', >+ }, >+ ], >+ age => '0', >+ }, >+); >+ >+C4::Items::ToggleNewStatus( { rules => \@rules } ); >+ >+$modified_item = C4::Items::GetItem( $itemnumber ); >+is( $modified_item->{new}, 'updated_value', q|ToggleNewStatus: The new value is not updated|); >+$marc_item = C4::Items::GetMarcItem( $biblionumber, $itemnumber ); >+is( $marc_item->subfield($tagfield, $new_tagfield), 'updated_value', q|ToggleNewStatus: The new value is not updated| ); >+ >+# Play with age >+$item = C4::Items::GetItem( $itemnumber ); >+my $dt_today = dt_from_string; >+my $days5ago = $dt_today->add_duration( DateTime::Duration->new( days => -5 ) ); >+ >+C4::Items::ModItem( { dateaccessioned => $days5ago }, $biblionumber, $itemnumber ); >+$item = C4::Items::GetItem( $itemnumber ); >+ >+@rules = ( >+ { >+ conditions => [ >+ { >+ field => 'items.ccode', >+ value => 'FIC', >+ }, >+ ], >+ substitutions => [ >+ { >+ field => 'items.new', >+ value => 'new_updated_value', >+ }, >+ ], >+ age => '10', >+ }, >+); >+C4::Items::ToggleNewStatus( { rules => \@rules } ); >+$modified_item = C4::Items::GetItem( $itemnumber ); >+is( $modified_item->{new}, 'updated_value', q|ToggleNewStatus: Age = 10 : The new value is not updated|); >+ >+$rules[0]->{age} = 5; >+$rules[0]->{substitutions}[0]{value} = 'new_updated_value5'; >+C4::Items::ToggleNewStatus( { rules => \@rules } ); >+$modified_item = C4::Items::GetItem( $itemnumber ); >+is( $modified_item->{new}, 'new_updated_value5', q|ToggleNewStatus: Age = 5 : The new value is updated|); >+ >+$rules[0]->{age} = ''; >+$rules[0]->{substitutions}[0]{value} = 'new_updated_value_empty_string'; >+C4::Items::ToggleNewStatus( { rules => \@rules } ); >+$modified_item = C4::Items::GetItem( $itemnumber ); >+is( $modified_item->{new}, 'new_updated_value_empty_string', q|ToggleNewStatus: Age = '' : The new value is updated|); >+ >+$rules[0]->{age} = undef; >+$rules[0]->{substitutions}[0]{value} = 'new_updated_value_undef'; >+C4::Items::ToggleNewStatus( { rules => \@rules } ); >+$modified_item = C4::Items::GetItem( $itemnumber ); >+is( $modified_item->{new}, 'new_updated_value_undef', q|ToggleNewStatus: Age = undef : The new value is updated|); >+ >+# Field deletion >+@rules = ( >+ { >+ conditions => [ >+ { >+ field => 'items.ccode', >+ value => 'FIC', >+ }, >+ ], >+ substitutions => [ >+ { >+ field => 'items.new', >+ value => '', >+ }, >+ ], >+ age => '0', >+ }, >+); >+ >+C4::Items::ToggleNewStatus( { rules => \@rules } ); >+ >+$modified_item = C4::Items::GetItem( $itemnumber ); >+is( $modified_item->{new}, '', q|ToggleNewStatus: The new value is empty|); >+$marc_item = C4::Items::GetMarcItem( $biblionumber, $itemnumber ); >+is( $marc_item->subfield($tagfield, $new_tagfield), undef, q|ToggleNewStatus: The new field is removed from the item marc| ); >+ >+# conditions multiple >+@rules = ( >+ { >+ conditions => [ >+ { >+ field => 'items.ccode', >+ value => 'FIC', >+ }, >+ { >+ field => 'items.homebranch', >+ value => 'CPL', >+ }, >+ ], >+ substitutions => [ >+ { >+ field => 'items.new', >+ value => 'new_value', >+ }, >+ ], >+ age => '0', >+ }, >+); >+ >+C4::Items::ToggleNewStatus( { rules => \@rules } ); >+ >+$modified_item = C4::Items::GetItem( $itemnumber ); >+is( $modified_item->{new}, 'new_value', q|ToggleNewStatus: conditions multiple: all match, the new value is updated|); >+ >+@rules = ( >+ { >+ conditions => [ >+ { >+ field => 'items.ccode', >+ value => 'FIC', >+ }, >+ { >+ field => 'items.homebranch', >+ value => 'DONT_EXIST', >+ }, >+ ], >+ substitutions => [ >+ { >+ field => 'items.new', >+ value => 'new_updated_value', >+ }, >+ ], >+ age => '0', >+ }, >+); >+ >+C4::Items::ToggleNewStatus( { rules => \@rules } ); >+ >+$modified_item = C4::Items::GetItem( $itemnumber ); >+is( $modified_item->{new}, 'new_value', q|ToggleNewStatus: conditions multiple: at least 1 condition does not match, the new value is not updated|); >+ >+@rules = ( >+ { >+ conditions => [ >+ { >+ field => 'items.ccode', >+ value => 'FIC|NFIC', >+ }, >+ { >+ field => 'items.homebranch', >+ value => 'MPL|CPL', >+ }, >+ ], >+ substitutions => [ >+ { >+ field => 'items.new', >+ value => 'new_updated_value', >+ }, >+ ], >+ age => '0', >+ }, >+); >+ >+C4::Items::ToggleNewStatus( { rules => \@rules } ); >+ >+$modified_item = C4::Items::GetItem( $itemnumber ); >+is( $modified_item->{new}, 'new_updated_value', q|ToggleNewStatus: conditions multiple: the 2 conditions match, the new value is updated|); >+ >+@rules = ( >+ { >+ conditions => [ >+ { >+ field => 'biblioitems.itemtype', >+ value => 'ITEMTYPE_T', >+ }, >+ ], >+ substitutions => [ >+ { >+ field => 'items.new', >+ value => 'another_new_updated_value', >+ }, >+ ], >+ age => '0', >+ }, >+); >+ >+C4::Items::ToggleNewStatus( { rules => \@rules } ); >+ >+$modified_item = C4::Items::GetItem( $itemnumber ); >+is( $modified_item->{new}, 'another_new_updated_value', q|ToggleNewStatus: conditions on biblioitems|); >diff --git a/tools/automatic_item_modification_by_age.pl b/tools/automatic_item_modification_by_age.pl >new file mode 100755 >index 0000000..eca339c >--- /dev/null >+++ b/tools/automatic_item_modification_by_age.pl >@@ -0,0 +1,119 @@ >+#!/usr/bin/perl >+ >+# This file is part of Koha. >+# >+# Copyright 2013 BibLibre >+# >+# Koha 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 3 of the License, or >+# (at your option) any later version. >+# >+# Koha 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. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+=head1 NAME >+ >+automatic_item_modification_by_age.pl: Update new status for items. >+ >+=cut >+ >+=head1 DESCRIPTION >+ >+This script allows a user to update the new status for items. >+ >+=cut >+ >+use Modern::Perl; >+ >+use CGI; >+use JSON qw( to_json from_json ); >+ >+use C4::Auth; >+use C4::Context; >+use C4::Items; >+use C4::Output; >+use C4::Koha; >+ >+my $cgi = new CGI; >+ >+# open template >+my ( $template, $loggedinuser, $cookie ) = get_template_and_user( >+ { >+ template_name => "tools/automatic_item_modification_by_age.tt", >+ query => $cgi, >+ type => "intranet", >+ authnotrequired => 0, >+ flagsrequired => { tools => 'items_batchmod' }, >+ } >+); >+ >+my $op = $cgi->param('op') // 'show'; >+ >+my $syspref_name = q|automatic_item_modification_by_age_configuration|; >+if ( $op eq 'update' ) { >+ my @rules; >+ my @unique_ids = $cgi->param('unique_id'); >+ for my $unique_id ( @unique_ids ) { >+ my @substitution_fields = $cgi->param("substitution_field_$unique_id"); >+ my @substitution_values = $cgi->param("substitution_value_$unique_id"); >+ my @condition_fields = $cgi->param("condition_field_$unique_id"); >+ my @condition_values = $cgi->param("condition_value_$unique_id"); >+ my $rule = { >+ substitutions => [], >+ conditions => [], >+ }; >+ for my $value ( @substitution_values ) { >+ my $field = shift @substitution_fields; >+ last unless $field; >+ push @{ $rule->{substitutions} }, { field => $field, value => $value }; >+ } >+ push @{ $rule->{substitutions} }, {} >+ unless @{ $rule->{substitutions} }; >+ for my $value ( @condition_values ) { >+ my $field = shift @condition_fields; >+ last unless $field; >+ push @{ $rule->{conditions} }, { field => $field, value => $value }; >+ } >+ push @{ $rule->{conditions} }, {} >+ unless @{ $rule->{conditions} }; >+ $rule->{age} = $cgi->param("age_$unique_id"); >+ push @rules, $rule; >+ } >+ my $syspref_content = to_json( \@rules ); >+ C4::Context->set_preference($syspref_name, $syspref_content); >+ >+ $op = 'show'; >+} >+ >+my @messages; >+my $syspref_content = C4::Context->preference($syspref_name); >+my $rules; >+$rules = eval { JSON::from_json( $syspref_content ) } >+ if $syspref_content; >+if ( $@ ) { >+ push @messages, { >+ type => 'error', >+ code => 'unable_to_load_configuration' >+ }; >+ $template->param( messages => \@messages ); >+ output_html_with_http_headers $cgi, $cookie, $template->output; >+ exit; >+} >+ >+my @item_fields = map { "items.$_" } C4::Items::columns; >+my @biblioitem_fields = map { "biblioitems.$_" } C4::Items::biblioitems_columns; >+$template->param( >+ op => $op, >+ messages => \@messages, >+ condition_fields => [ @item_fields, @biblioitem_fields ], >+ substitution_fields => \@item_fields, >+ rules => $rules, >+); >+ >+output_html_with_http_headers $cgi, $cookie, $template->output; >-- >1.9.1
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 11023
:
21926
|
21932
|
21982
|
21983
|
22004
|
22386
|
22734
|
22735
|
24068
|
24069
|
24070
|
24146
|
24147
|
24148
|
25767
|
25833
|
26817
|
26818
|
26819
|
26820
|
26821
|
26822
|
26855
|
27810
|
29216
|
29217
|
29218
|
29219
|
29220
|
29221
|
29222
|
31820
|
31821
|
31822
|
33226
|
33227
|
33228
|
33229
|
33230
|
33231
|
33232
|
33233
|
33234
|
33235
|
34782
|
34783
|
34784
|
34785
|
34786
|
34787
|
34788
|
34789
|
34790
|
34791
|
34870
|
34871
|
34872
|
34873
|
34874
|
34875
|
34876
|
34877
|
34878
|
34879
|
34880
|
34881
|
34882
|
34883
|
35375
|
35376
| 41147 |
41148