Bug 22049: Make MarkIssueReturned rely on returndate only
[koha.git] / misc / export_records.pl
index b411425..a1eecc8 100755 (executable)
@@ -24,15 +24,38 @@ use Pod::Usage;
 
 use C4::Auth;
 use C4::Context;
-use C4::Csv;
 use C4::Record;
 
 use Koha::Biblioitems;
 use Koha::Database;
+use Koha::CsvProfiles;
 use Koha::Exporter::Record;
 use Koha::DateUtils qw( dt_from_string output_pref );
 
-my ( $output_format, $timestamp, $dont_export_items, $csv_profile_id, $deleted_barcodes, $clean, $filename, $record_type, $id_list_file, $starting_authid, $ending_authid, $authtype, $starting_biblionumber, $ending_biblionumber, $itemtype, $starting_callnumber, $ending_callnumber, $start_accession, $end_accession, $help );
+my (
+    $output_format,
+    $timestamp,
+    $dont_export_items,
+    $csv_profile_id,
+    $deleted_barcodes,
+    $clean,
+    $filename,
+    $record_type,
+    $id_list_file,
+    $starting_authid,
+    $ending_authid,
+    $authtype,
+    $starting_biblionumber,
+    $ending_biblionumber,
+    $itemtype,
+    $starting_callnumber,
+    $ending_callnumber,
+    $start_accession,
+    $end_accession,
+    $marc_conditions,
+    $help
+);
+
 GetOptions(
     'format=s'                => \$output_format,
     'date=s'                  => \$timestamp,
@@ -53,6 +76,7 @@ GetOptions(
     'ending_callnumber=s'     => \$ending_callnumber,
     'start_accession=s'       => \$start_accession,
     'end_accession=s'         => \$end_accession,
+    'marc_conditions=s'       => \$marc_conditions,
     'h|help|?'                => \$help
 ) || pod2usage(1);
 
@@ -71,6 +95,9 @@ if ( $output_format eq 'csv' and $record_type eq 'auths' ) {
     pod2usage(q|CSV output is only available for biblio records|);
 }
 
+if ( $output_format eq 'csv' and not $csv_profile_id ) {
+    pod2usage(q|Define a csv profile to export in CSV|);
+}
 
 if ( $timestamp and $record_type ne 'bibs' ) {
     pod2usage(q|--timestamp can only be used with biblios|);
@@ -87,6 +114,22 @@ if ( $deleted_barcodes and $record_type ne 'bibs' ) {
 $start_accession = dt_from_string( $start_accession ) if $start_accession;
 $end_accession   = dt_from_string( $end_accession )   if $end_accession;
 
+# Parse marc conditions
+my @marc_conditions;
+if ($marc_conditions) {
+    foreach my $condition (split(/,\s*/, $marc_conditions)) {
+        if ($condition =~ /^(\d{3})([\w\d]?)(=|(?:!=)|>|<)([^,]+)$/) {
+            push @marc_conditions, [$1, $2, $3, $4];
+        }
+        elsif ($condition =~ /^(exists|not_exists)\((\d{3})([\w\d]?)\)$/) {
+            push @marc_conditions, [$2, $3, $1 eq 'exists' ? '?' : '!?'];
+        }
+        else {
+            die("Invalid condititon: $condition");
+        }
+    }
+}
+
 my $dbh = C4::Context->dbh;
 
 # Redirect stdout
@@ -95,22 +138,22 @@ open STDOUT, '>', $filename if $filename;
 
 my @record_ids;
 
-$timestamp = ($timestamp) ? output_pref({ dt => dt_from_string($timestamp), dateformat => 'iso', dateonly => 1, }): '';
+$timestamp = ($timestamp) ? output_pref({ dt => dt_from_string($timestamp), dateformat => 'iso', dateonly => 0, }): '';
 
 if ( $record_type eq 'bibs' ) {
     if ( $timestamp ) {
         push @record_ids, $_->{biblionumber} for @{
             $dbh->selectall_arrayref(q| (
-                SELECT biblionumber
-                FROM biblioitems
+                SELECT biblio_metadata.biblionumber
+                FROM biblio_metadata
                   LEFT JOIN items USING(biblionumber)
-                WHERE biblioitems.timestamp >= ?
+                WHERE biblio_metadata.timestamp >= ?
                   OR items.timestamp >= ?
             ) UNION (
-                SELECT biblionumber
-                FROM biblioitems
+                SELECT biblio_metadata.biblionumber
+                FROM biblio_metadata
                   LEFT JOIN deleteditems USING(biblionumber)
-                WHERE biblioitems.timestamp >= ?
+                WHERE biblio_metadata.timestamp >= ?
                   OR deleteditems.timestamp >= ?
             ) |, { Slice => {} }, ( $timestamp ) x 4 );
         };
@@ -144,7 +187,7 @@ if ( $record_type eq 'bibs' ) {
                 ?
                   C4::Context->preference('item-level_itypes')
                     ? ( 'items.itype' => $itemtype )
-                    : ( 'biblioitems.itemtype' => $itemtype )
+                    : ( 'me.itemtype' => $itemtype )
                 : ()
             ),
 
@@ -173,8 +216,9 @@ elsif ( $record_type eq 'auths' ) {
 }
 
 @record_ids = uniq @record_ids;
-if ( @record_ids and my $id_list_file ) {
-    my @filter_record_ids = <$id_list_file>;
+if ( @record_ids and $id_list_file ) {
+    open my $fh, '<', $id_list_file or die "Cannot open file $id_list_file ($!)";
+    my @filter_record_ids = <$fh>;
     @filter_record_ids = map { my $id = $_; $id =~ s/[\r\n]*$//; $id } @filter_record_ids;
     # intersection
     my %record_ids = map { $_ => 1 } @record_ids;
@@ -183,22 +227,21 @@ if ( @record_ids and my $id_list_file ) {
 
 if ($deleted_barcodes) {
     for my $record_id ( @record_ids ) {
-        my $q = q|
-        |;
-        my $barcode = $dbh->selectall_arrayref(q| (
+        my $barcode = $dbh->selectall_arrayref(q|
             SELECT DISTINCT barcode
             FROM deleteditems
             WHERE deleteditems.biblionumber = ?
         |, { Slice => {} }, $record_id );
-        say $_->{barcode} for @$barcode
+        say $_->{barcode} for @$barcode;
     }
 }
 else {
     Koha::Exporter::Record::export(
         {   record_type        => $record_type,
             record_ids         => \@record_ids,
+            record_conditions  => @marc_conditions ? \@marc_conditions : undef,
             format             => $output_format,
-            csv_profile_id     => ( $csv_profile_id || GetCsvProfileId( C4::Context->preference('ExportWithCsvProfile') ) || undef ),
+            csv_profile_id     => $csv_profile_id,
             export_items       => (not $dont_export_items),
             clean              => $clean || 0,
         }
@@ -213,7 +256,7 @@ export records - This script exports record (biblios or authorities)
 
 =head1 SYNOPSIS
 
-export_records.pl [-h|--help] [--format=format] [--date=date] [--record-type=TYPE] [--dont_export_items] [--deleted_barcodes] [--clean] [--id_list_file=PATH] --filename=outputfile
+export_records.pl [-h|--help] [--format=format] [--date=datetime] [--record-type=TYPE] [--dont_export_items] [--deleted_barcodes] [--clean] [--id_list_file=PATH] --filename=outputfile
 
 =head1 OPTIONS
 
@@ -229,10 +272,10 @@ Print a brief help message.
 
 =item B<--date>
 
- --date=DATE            DATE should be entered as the 'dateformat' syspref is
-                        set (dd/mm/yyyy for metric, yyyy-mm-dd for iso,
-                        mm/dd/yyyy for us) records exported are the ones that
-                        have been modified since DATE.
+ --date=DATETIME        DATETIME should be entered as the 'dateformat' syspref is
+                        set (dd/mm/yyyy[ hh:mm:ss] for metric, yyyy-mm-dd[ hh:mm:ss] for iso,
+                        mm/dd/yyyy[ hh:mm:ss] for us) records exported are the ones that
+                        have been modified since DATETIME.
 
 =item B<--record-type>
 
@@ -245,7 +288,6 @@ Print a brief help message.
 =item B<--csv_profile_id>
 
  --csv_profile_id=ID    Generate a CSV file with the given CSV profile id (see tools/csv-profiles.pl)
-                        Unless provided, the one defined in the system preference 'ExportWithCsvProfile' will be used.
                         This can only be used to export biblio records.
 
 =item B<--deleted_barcodes>
@@ -309,6 +351,28 @@ Print a brief help message.
 
  --end_accession=DATE           Export biblio with an item accessionned after DATE
 
+=item B<--marc_conditions>
+
+ --marc_conditions=CONDITIONS   Only include biblios with MARC data matching CONDITIONS.
+                                CONDITIONS is on the format: <marc_target><binary_operator><value>,
+                                or <unary_operation>(<marc_target>).
+                                with multiple conditions separated by commas (,).
+                                For example: --marc_conditions="035a!=(EXAMPLE)123,041a=swe".
+                                Multiple conditions are all required to match.
+                                If <marc_target> has multiple values all values
+                                are also required to match.
+                                Valid operators are: = (equal to), != (not equal to),
+                                > (great than) and < (less than).
+
+                                Two unary operations are also supported:
+                                exists(<marc_target>) and not_exists(<marc_target>).
+                                For example: --marc_conditions="exists(035a)".
+
+                                "exists(<marc_target)" will include marc records where
+                                <marc_target> exists regardless of target value, and
+                                "exists(<marc_target>)" will include marc records where
+                                no <marc_target> exists.
+
 =back
 
 =head1 AUTHOR