From c631fa0924ba54f42dc70616f5462d4b277f28b8 Mon Sep 17 00:00:00 2001 From: Jonathan Druart 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 Signed-off-by: Kyle M Hall 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 Signed-off-by: Kyle M Hall Bug 11023: Add UT for C4::Items::ToggleNewStatus Test plan: prove t/db_dependent/Items/ToggleNewStatus.t Signed-off-by: juliette et remy Signed-off-by: Kyle M Hall 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 Bug 11023: Use DBIx-Class to retrieve column names Signed-off-by: Kyle M Hall 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 Bug 11023: Rename the duration parameter with 'age' Signed-off-by: Kyle M Hall 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 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 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 Bug 11023 [QA Followup] - Complete the renaming of "toggle new status" to "automatic item modification by age" Signed-off-by: Kyle M Hall Signed-off-by: Brendan A Gallagher --- C4/Items.pm | 96 +++++- .../prog/en/css/staff-global.css | 30 ++ .../automatic_item_modification_by_age.tt | 45 +++ .../automatic_item_modification_by_age.tt | 294 ++++++++++++++++++ .../prog/en/modules/tools/tools-home.tt | 5 + .../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 26e9342a6b..51479ecfeb 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, @@ -2198,7 +2200,8 @@ sub _koha_new_item { 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 }); @@ -2242,6 +2245,7 @@ sub _koha_new_item { $item->{'more_subfields_xml'}, $item->{'copynumber'}, $item->{'stocknumber'}, + $item->{'new'}, ); my $itemnumber; @@ -3135,4 +3139,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 b8a857d47c..23283048d2 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css +++ b/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css @@ -2815,6 +2815,7 @@ span.onsite_checkout { border-radius: 4px; border : 1px solid #FFF2CE; } + div.lastchecked { padding : .2em 1em; border: 2px solid #BCDB89; @@ -2845,3 +2846,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 0000000000..8fdda1ba4e --- /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' %] +

Automatic item modifications by age configuration

+ +

This configuration page allows to configure the rules for the automatic item modifications by age cronjob script.

+ +

Libraries can manage the 'new' status for items. With this script, it will be possible to:

+

    +
  • know easily what are the new items in the catalogue.
  • +
  • display an icon in the search results for new items.
  • +
  • configure issuing rules depending the 'new' status.
  • +
  • get a RSS/Atom feeds on these new items.
  • +
+ +

How to work the configuration page?

+

There are 3 values to define:

+

The duration

+

This value corresponds to the duration an item is considered as new.

+

The conditions

+

Conditions should be defined if you want to test some values before to substitute fields in the items.

+

They are cumulatives but you can separate with a pipe '|' for a field with several values.

+

The substitutions

+

Substitutions are changes to apply to the matching items.

+

At least one substitution must be defined, else there is no sense to launch the script.

+

If the value is an empty string, the field will be deleted.

+

Examples

+

You want to remove the items.new value for items created 10 days ago:

+
    +
  • Duration: 10 days
  • +
  • No condition
  • +
  • Substitution: items.new = '' (no value in the input)
  • +
+ +

You want to change the items.ccode=1 to items.ccode=2 for items created 7 days ago. +

    +
  • Duration: 7 days
  • +
  • Condition: items.ccode = 1
  • +
  • Substitution: items.ccode = 2
  • +
+ +

How to execute the cronjob script?

+

The cronjob script is misc/cronjobs/automatic_item_modification_by_age.pl.

+

Try the -h parameter in order to see the help.

+

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.

+ +[% 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 0000000000..a08e96d022 --- /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' %] +Koha › Tools › Automatic item modifications by age +[% INCLUDE 'doc-head-close.inc' %] + + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'cat-search.inc' %] + + +
+
+
+
+

Automatic item modifications by age

+
+ Edit +
+ [% FOR message IN messages %] + [% IF message.type == "error" %] +
+ [% END %] + [% IF message.code == "unable_to_load_configuration" %] + An error occurs: Unable to load the configuration. + [% END %] +
+ [% END %] + + [% IF op == 'edit_form' %] +
+
+

List of rules

+
+ [% FOR rule IN rules %] + [% SET id = loop.count %] +
+ +
+
Age
+ days +
+
+
Conditions
+ [% FOR condition IN rule.conditions %] +
+ + = + + + +
+ [% END %] +
+
+
Substitutions
+ [% FOR substitution IN rule.substitutions %] +
+ + = + + + +
+ [% END %] +
+ Remove this rule +
+ [% END %] +
+
+ There is no rule defined. +
+
+ + Cancel + +
+
+
+

Add a new rule

+
+ +
+
Age
+ days +
+
+
Conditions
+
+ + = + + + +
+
+
+
Substitutions
+
+ + = + + + +
+
+ Add this rule + Remove this rule +
+ [% ELSIF rules and op == 'show' %] +
+

List of rules

+ [% FOR rule IN rules %] +
+
+
Age
+ [% IF rule.age.defined and rule.age.length > 0 %] + [% rule.age %] days + [% ELSE %] + There is no age for this rule. + [% END %] +
+
+
Conditions
+ [% FOR condition IN rule.conditions %] + [% IF condition.field %] +
+ [% condition.field %] = [% condition.value %] +
+ [% ELSE %] + There is no condition for this rule. + [% END %] + [% END %] +
+
+
Substitutions
+ [% FOR substitution IN rule.substitutions %] +
+ [% substitution.field %] = [% substitution.value %] +
+ [% END %] +
+
+ [% END %] +
+ [% ELSE %] + There is no rule defined. Please click on the edit button. + [% END %] + +
+
+
+ [% INCLUDE 'tools-menu.inc' %] +
+
+[% 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 629ce84726..bb54f54642 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 @@ -129,6 +129,11 @@
Modify a batch of records (biblios or authorities)
[% END %] + [% IF ( CAN_user_tools_items_batchmod ) %] +
Automatic item modifications by age
+
Define rules to modify items by age
+ [% END %] + [% IF ( CAN_user_tools_export_catalog ) %]
Export data
Export bibliographic, holdings, and authority records
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 0000000000..51effd32b1 --- /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 + +=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 . + +=cut diff --git a/t/db_dependent/Items/AutomaticItemModificationByAge.t b/t/db_dependent/Items/AutomaticItemModificationByAge.t new file mode 100644 index 0000000000..3f2943c942 --- /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 0000000000..eca339cb39 --- /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 . + +=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; -- 2.39.5