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;
location => undef,
permanent_location => undef,
materials => undef,
+ new => undef,
notforloan => 0,
# paidfor => undef, # commented, see bug 12817
price => undef,
enumchron = ?,
more_subfields_xml = ?,
copynumber = ?,
- stocknumber = ?
+ stocknumber = ?,
+ new = ?
";
my $sth = $dbh->prepare($query);
my $today = output_pref({ dt => dt_from_string, dateformat => 'iso', dateonly => 1 });
$item->{'more_subfields_xml'},
$item->{'copynumber'},
$item->{'stocknumber'},
+ $item->{'new'},
);
my $itemnumber;
};
}
+=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;
border-radius: 4px;
border : 1px solid #FFF2CE;
}
+
div.lastchecked {
padding : .2em 1em;
border: 2px solid #BCDB89;
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;
+}
--- /dev/null
+[% 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' %]
--- /dev/null
+[% 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' %]
<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>
--- /dev/null
+#!/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
--- /dev/null
+#!/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|);
--- /dev/null
+#!/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;