From bfd6697c4e60ba6f6f97db7f535abe8563dd7bf6 Mon Sep 17 00:00:00 2001
From: Alex Arnaud <alex.arnaud@biblibre.com>
Date: Wed, 9 Dec 2015 11:09:58 +0100
Subject: [PATCH] Bug 15336 - New command-line script: merge_bookseller.pl
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Test plan (i.e. merge bookseller n° 3 into the n° 6):

run perl misc/migration_tools/merge_booksellers.pl -f 3 -t 6 -v -c

Check that basketgroups, baskets, contacts, contracts and invoices
had moved into bookseller 6.
Check that bookseller n° 3 has been deleted.

Remove -c (confirm) parameter to run in test mode only.

Signed-off-by: Marc Véron <veron@veron.ch>
---
 Koha/Acquisition/Bookseller.pm        |  29 ++++++++
 misc/migration_tools/merge_vendors.pl | 120 ++++++++++++++++++++++++++++++++++
 t/Bookseller.t                        |  99 +++++++++++++++++++++++++++-
 t/db_dependent/Bookseller.t           | 105 ++++++++++++++++++++++++++++-
 4 files changed, 349 insertions(+), 4 deletions(-)
 create mode 100644 misc/migration_tools/merge_vendors.pl

diff --git a/Koha/Acquisition/Bookseller.pm b/Koha/Acquisition/Bookseller.pm
index e32b010..d19df66 100644
--- a/Koha/Acquisition/Bookseller.pm
+++ b/Koha/Acquisition/Bookseller.pm
@@ -26,6 +26,35 @@ sub subscriptions {
     return Koha::Subscriptions->search( { aqbooksellerid => $self->id } );
 }
 
+sub merge_with {
+    my ($self, $booksellerid) = @_;
+
+    my $result = {};
+
+    unless ( Koha::Acquisition::Booksellers->find( $booksellerid ) ) {
+        die("Unknown target bookseller number: $booksellerid");
+    }
+
+    foreach my $entity (qw(Aqbasketgroup Aqbasket Aqcontract Aqcontact Aqinvoice Deleteditem Item Subscription)) {
+        my $rs = Koha::Database->new()->schema->resultset($entity);
+
+        my $foreign_key = $entity eq 'Subscription' ? 'aqbooksellerid' : 'booksellerid';
+        $rs = $rs->search({$foreign_key => $self->id });
+
+        while (my $e = $rs->next) {
+            $e->set_column($foreign_key, $booksellerid);
+            $e->update();
+            push @{ $result->{$entity} }, $e->id();
+        }
+    }
+    return $result;
+}
+
+# TODO Move code from ModBookseller
+sub update {
+    die "not implemented yet";
+}
+
 sub _type {
     return 'Aqbookseller';
 }
diff --git a/misc/migration_tools/merge_vendors.pl b/misc/migration_tools/merge_vendors.pl
new file mode 100644
index 0000000..1471c74
--- /dev/null
+++ b/misc/migration_tools/merge_vendors.pl
@@ -0,0 +1,120 @@
+#!/usr/bin/perl
+
+# Copyright 2015 BibLibre
+#
+# 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>.
+
+use Modern::Perl;
+
+use Koha::Acquisition::Bookseller;
+use C4::Acquisition;
+use C4::Contract;
+
+use Getopt::Long;
+use Pod::Usage;
+
+my ($help, $verbose, $mergefrom, $mergeto, $confirm);
+GetOptions(
+    'help|h' => \$help,
+    'verbose|v' => \$verbose,
+    'from|f:s' => \$mergefrom,
+    'to|t:s' => \$mergeto,
+    'confirm|c' => \$confirm,
+) || pod2usage(1);
+
+if ($help || !$mergefrom || !$mergeto) {
+    pod2usage(1);
+}
+
+if ($mergefrom eq $mergeto) {
+    pod2usage('Source and target vendors should be different.');
+}
+
+my $schema = Koha::Database->schema;
+$schema->storage->txn_begin;
+
+my $from_vendor = Koha::Acquisition::Booksellers->find( $mergefrom );
+die "Unknown vendor id ($mergefrom)" unless $from_vendor;
+
+my $report = $from_vendor->merge_with($mergeto);
+
+if ($confirm) {
+    $schema->storage->txn_commit;
+}
+else {
+    $schema->storage->txn_rollback;
+}
+
+Koha::Acquisition::Booksellers->find($mergefrom)->delete if $confirm;
+
+if ($verbose) {
+    print_report($report);
+}
+
+print "Nothing done (test only). Use confirm option to commit changes in database\n" unless $confirm;
+
+sub print_report {
+    my $report = shift;
+
+    foreach my $entity (keys %$report) {
+        my $count = scalar @{ $report->{$entity} };
+        my $ids = join(', ', @{ $report->{$entity} });
+        print "$entity merged: $count (numbers: $ids)\n";
+    }
+}
+
+
+=head1 NAME
+
+merge source vendor (-f) into target vendor (-t)
+
+=head1 SYNOPSIS
+
+** WARNING ** This script can delete data. You must only use it if you know what your are doing
+
+merge_vendors.pl [-h|--help] [-v|--verbose] [-c|--confirm] --from=vendorid --to=vendorid
+
+=head1 OPTIONS
+
+=over
+
+=item B<-h|--help>
+
+Print a brief help message.
+
+=item B<--verbose>
+
+ --verbose              Show more information on what is done.
+
+=item B<--confirm>
+
+ --confirm              Commit the changes in database. Running without this
+                        parameter is like testing. It is recommanded to run without
+                        --confirm first to be sure of the changes.
+
+=item B<--from>
+
+ --from=<vendorid>  The source vendor identifier that will be merged in
+                        target vendor.
+
+=item B<--to>
+
+ --to=<vendorid>    The target vendor identifier in which merge
+                        source vendor.
+
+=back
+
+=cut
diff --git a/t/Bookseller.t b/t/Bookseller.t
index 360a0ad..6d6aab3 100755
--- a/t/Bookseller.t
+++ b/t/Bookseller.t
@@ -1,14 +1,107 @@
 #!/usr/bin/perl
 #
-# This Koha test module is a stub!  
+# This Koha test module is a stub!
 # Add more tests here!!!
 
 use strict;
 use warnings;
 
-use Test::More tests => 1;
+use C4::Acquisition qw( GetBasketgroup GetBasket GetInvoice );
+use C4::Contract qw( GetContract );
+use C4::Items qw ( GetItem );
+use C4::Serials qw( GetSubscription );
+use t::lib::TestBuilder;
+
+use Test::More tests => 14;
 
 BEGIN {
-        use_ok('C4::Bookseller');
+        use_ok('Koha::Acquisition::Bookseller');
+}
+
+my $schema = Koha::Database->schema;
+$schema->storage->txn_begin;
+
+our $builder = t::lib::TestBuilder->new;
+
+my $bookseller1 = create('Aqbookseller');
+my $booksellerid1 = $bookseller1->{id};
+my $bookseller2 = create('Aqbookseller');
+my $booksellerid2 = $bookseller2->{id};
+
+my $bg1 = create('Aqbasketgroup', $booksellerid1);
+my $bg2 = create('Aqbasketgroup', $booksellerid1);
+my $bg3 = create('Aqbasketgroup', $booksellerid2);
+
+my $basket1 = create('Aqbasket', $booksellerid1);
+my $basket2 = create('Aqbasket', $booksellerid1);
+
+my $contract = create('Aqcontract', $booksellerid1);
+
+my $contact = create('Aqcontact', $booksellerid1);
+
+my $invoice = create('Aqinvoice', $booksellerid1);
+
+my $deleteditem = create('Deleteditem', $booksellerid1);
+
+my $item1 = create('Item', $booksellerid1);
+
+my $subscription = create('Subscription', $booksellerid1);
+
+my $bookseller = Koha::Acquisition::Bookseller->fetch({id => $booksellerid1});
+
+my ($error, $result) = $bookseller->merge_to(345876876);
+is($error, "Unknown target bookseller number: 345876876", 'Merge to unknown bookseller return an error' );
+
+($error, $result) = $bookseller->merge_to($booksellerid2);
+my $expected_result = {
+    Aqbasketgroup => [$bg1->{id}, $bg2->{id}],
+    Aqbasket => [$basket1->{basketno}, $basket2->{basketno}],
+    Aqcontract => [$contract->{contractnumber}],
+    Aqcontact => [$contact->{id}],
+    Aqinvoice => [$invoice->{invoiceid}],
+    Deleteditem => [$deleteditem->{itemnumber}],
+    Item => [$item1->{itemnumber}],
+    Subscription => [$subscription->{subscriptionid}]
+};
+
+is_deeply($result, $expected_result, 'Result should be as expected');
+
+is(GetBasketgroup($bg1->{id})->{'booksellerid'}, $booksellerid2, 'Basket group 1 has bookseller 2');
+
+is(GetBasketgroup($bg2->{id})->{'booksellerid'}, $booksellerid2, 'Basket group 2 has bookseller 2');
+
+is(GetBasketgroup($bg3->{id})->{'booksellerid'}, $booksellerid2, 'Basket group 3 keep bookseller 2');
+
+is(GetBasket($basket1->{basketno})->{'booksellerid'}, $booksellerid2, 'Basket 1 has bookseller 2');
+
+is(GetBasket($basket2->{basketno})->{'booksellerid'}, $booksellerid2, 'Basket 2 has bookseller 2');
+
+is(GetContract({contractnumber => $contract->{contractnumber}})->{'booksellerid'}, $booksellerid2, 'Basket 1 has bookseller 2');
+
+is(C4::Bookseller::Contact->fetch($contact->{id})->{'booksellerid'}, $booksellerid2, 'Contact 1 has booksellerid 2');
+
+is(GetInvoice($invoice->{invoiceid})->{'booksellerid'}, $booksellerid2, 'Invoice has bookseller 2');
+
+my $item = Koha::Database->new()->schema->resultset('Deleteditem')
+    ->find({itemnumber => $deleteditem->{itemnumber}});
+is($item->get_column('booksellerid'), $booksellerid2, 'Deleted item has bookseller 2');
+
+is(GetItem($item1->{itemnumber})->{'booksellerid'}, $booksellerid2, 'Item has bookseller 2');
+
+is(GetSubscription($subscription->{subscriptionid})->{'aqbooksellerid'}, $booksellerid2, 'Subscription has bookseller 2');
+
+$schema->storage->txn_rollback;
+
+sub create {
+    my ($source, $booksellerid) = @_;
+
+    my $build = { source => $source};
+    my $foreign_key = $source eq 'Subscription' ? 'aqbooksellerid' : 'booksellerid';
+    $build->{value} = {$foreign_key => $booksellerid} if $booksellerid;
+
+    my $entity = $builder->build($build);
+
+    return $entity;
 }
 
+1;
diff --git a/t/db_dependent/Bookseller.t b/t/db_dependent/Bookseller.t
index 77cf3ef..f96e35b 100644
--- a/t/db_dependent/Bookseller.t
+++ b/t/db_dependent/Bookseller.t
@@ -2,7 +2,7 @@
 
 use Modern::Perl;
 
-use Test::More tests => 86;
+use Test::More tests => 99;
 use Test::MockModule;
 use Test::Warn;
 
@@ -11,9 +11,12 @@ use Koha::DateUtils;
 use DateTime::Duration;
 use t::lib::Mocks;
 use C4::Acquisition;
+use C4::Contract qw( GetContract );
+use C4::Items qw ( GetItem );
 use C4::Serials;
 use C4::Budgets;
 use C4::Biblio;
+use t::lib::TestBuilder;
 
 use Koha::Acquisition::Booksellers;
 use Koha::Acquisition::Order;
@@ -38,6 +41,8 @@ $schema->storage->txn_begin();
 
 $dbh->{RaiseError} = 1;
 
+our $builder = t::lib::TestBuilder->new;
+
 #Start tests
 $dbh->do(q|DELETE FROM aqorders|);
 $dbh->do(q|DELETE FROM aqbasket|);
@@ -751,6 +756,91 @@ is( $first_contact->phone,
 is( $contacts->count,
     1, 'Only one contact after modification' );
 
+my $bookseller1   = create('Aqbookseller');
+my $booksellerid1 = $bookseller1->{id};
+my $bookseller2   = create('Aqbookseller');
+my $booksellerid2 = $bookseller2->{id};
+
+my $bg1 = create( 'Aqbasketgroup', $booksellerid1 );
+my $bg2 = create( 'Aqbasketgroup', $booksellerid1 );
+my $bg3 = create( 'Aqbasketgroup', $booksellerid2 );
+
+my $basket1 = create( 'Aqbasket', $booksellerid1 );
+my $basket2 = create( 'Aqbasket', $booksellerid1 );
+
+my $contract = create( 'Aqcontract', $booksellerid1 );
+
+my $contact = create( 'Aqcontact', $booksellerid1 );
+
+my $invoice = create( 'Aqinvoice', $booksellerid1 );
+
+my $deleteditem = create( 'Deleteditem', $booksellerid1 );
+
+my $item1 = create( 'Item', $booksellerid1 );
+
+my $subscription = create( 'Subscription', $booksellerid1 );
+
+$bookseller = Koha::Acquisition::Booksellers->find( $booksellerid1 );
+
+my $result = eval { $bookseller->merge_with(345876876) };
+ok(
+    $@ =~ /^Unknown target bookseller number: 345876876/,
+    'Merge to unknown bookseller return an error'
+);
+
+$result = $bookseller->merge_with($booksellerid2);
+my $expected_result = {
+    Aqbasketgroup => [ $bg1->{id},           $bg2->{id} ],
+    Aqbasket      => [ $basket1->{basketno}, $basket2->{basketno} ],
+    Aqcontract    => [ $contract->{contractnumber} ],
+    Aqcontact     => [ $contact->{id} ],
+    Aqinvoice     => [ $invoice->{invoiceid} ],
+    Deleteditem   => [ $deleteditem->{itemnumber} ],
+    Item          => [ $item1->{itemnumber} ],
+    Subscription  => [ $subscription->{subscriptionid} ]
+};
+
+is_deeply( $result, $expected_result, 'Result should be as expected' );
+
+is( GetBasketgroup( $bg1->{id} )->{'booksellerid'},
+    $booksellerid2, 'Basket group 1 has bookseller 2' );
+
+is( GetBasketgroup( $bg2->{id} )->{'booksellerid'},
+    $booksellerid2, 'Basket group 2 has bookseller 2' );
+
+is( GetBasketgroup( $bg3->{id} )->{'booksellerid'},
+    $booksellerid2, 'Basket group 3 keep bookseller 2' );
+
+is( GetBasket( $basket1->{basketno} )->{'booksellerid'},
+    $booksellerid2, 'Basket 1 has bookseller 2' );
+
+is( GetBasket( $basket2->{basketno} )->{'booksellerid'},
+    $booksellerid2, 'Basket 2 has bookseller 2' );
+
+is(
+    GetContract( { contractnumber => $contract->{contractnumber} } )
+      ->{'booksellerid'},
+    $booksellerid2,
+    'Basket 1 has bookseller 2'
+);
+
+is( Koha::Acquisition::Bookseller::Contacts->find( $contact->{id} )->booksellerid,
+    $booksellerid2, 'Contact 1 has booksellerid 2' );
+
+is( GetInvoice( $invoice->{invoiceid} )->{'booksellerid'},
+    $booksellerid2, 'Invoice has bookseller 2' );
+
+my $item = Koha::Database->new()->schema->resultset('Deleteditem')
+  ->find( { itemnumber => $deleteditem->{itemnumber} } );
+is( $item->get_column('booksellerid'),
+    $booksellerid2, 'Deleted item has bookseller 2' );
+
+is( GetItem( $item1->{itemnumber} )->{'booksellerid'},
+    $booksellerid2, 'Item has bookseller 2' );
+
+is( GetSubscription( $subscription->{subscriptionid} )->{'aqbooksellerid'},
+    $booksellerid2, 'Subscription has bookseller 2' );
+
 #End transaction
 $schema->storage->txn_rollback();
 
@@ -774,3 +864,16 @@ sub field_filter {
     }
     return $struct;
 }
+
+sub create {
+    my ( $source, $booksellerid ) = @_;
+
+    my $build = { source => $source };
+    my $foreign_key =
+      $source eq 'Subscription' ? 'aqbooksellerid' : 'booksellerid';
+    $build->{value} = { $foreign_key => $booksellerid } if $booksellerid;
+
+    my $entity = $builder->build($build);
+
+    return $entity;
+}
-- 
2.1.4