PriceTaxCalculator: chart->taxkey lookup vorberechnen
[kivitendo-erp.git] / scripts / rose_auto_create_model.pl
index bb246f8..fb589c6 100755 (executable)
@@ -14,7 +14,9 @@ use Digest::MD5 qw(md5_hex);
 use English qw( -no_match_vars );
 use Getopt::Long;
 use List::MoreUtils qw(none);
 use English qw( -no_match_vars );
 use Getopt::Long;
 use List::MoreUtils qw(none);
+use List::UtilsBy qw(partition_by);
 use Pod::Usage;
 use Pod::Usage;
+use Rose::DB::Object 0.809;
 use Term::ANSIColor;
 
 use SL::Auth;
 use Term::ANSIColor;
 
 use SL::Auth;
@@ -45,17 +47,19 @@ our $manager_path = "SL/DB/Manager";
 
 my %config;
 
 
 my %config;
 
-our %foreign_key_name_map = (
-  oe                   => { payment => 'payment_terms', },
-  ar                   => { payment => 'payment_terms', },
-  ap                   => { payment => 'payment_terms', },
+our %foreign_key_name_map     = (
+  KIVITENDO                   => {
+    oe                        => { payment => 'payment_terms', },
+    ar                        => { payment => 'payment_terms', },
+    ap                        => { payment => 'payment_terms', },
 
 
-  orderitems           => { parts => 'part', trans => 'order', },
-  delivery_order_items => { parts => 'part' },
-  invoice              => { parts => 'part' },
-  follow_ups           => { 'employee_obj' => 'created_for' },
+    orderitems                => { parts => 'part', trans => 'order', },
+    delivery_order_items      => { parts => 'part' },
+    invoice                   => { parts => 'part' },
+    follow_ups                => { 'employee_obj' => 'created_for' },
 
 
-  periodic_invoices_configs => { oe => 'order' },
+    periodic_invoices_configs => { oe => 'order' },
+  },
 );
 
 sub setup {
 );
 
 sub setup {
@@ -86,11 +90,10 @@ sub setup {
 }
 
 sub process_table {
 }
 
 sub process_table {
-  my @spec       =  @_;
-  my $table      =  $spec[0];
+  my ($domain, $table, $package) = @_;
   my $schema     = '';
   ($schema, $table) = split(m/\./, $table) if $table =~ m/\./;
   my $schema     = '';
   ($schema, $table) = split(m/\./, $table) if $table =~ m/\./;
-  my $package    =  ucfirst($spec[1] || $spec[0]);
+  $package       =  ucfirst($package || $table);
   $package       =~ s/_+(.)/uc($1)/ge;
   my $meta_file  =  "${meta_path}/${package}.pm";
   my $mngr_file  =  "${manager_path}/${package}.pm";
   $package       =~ s/_+(.)/uc($1)/ge;
   my $meta_file  =  "${meta_path}/${package}.pm";
   my $mngr_file  =  "${manager_path}/${package}.pm";
@@ -138,7 +141,8 @@ CODE
   if ($foreign_key_definition && ($definition =~ /\Q$foreign_key_definition\E/)) {
     my ($start, $end) = ($-[0], $+[0]);
 
   if ($foreign_key_definition && ($definition =~ /\Q$foreign_key_definition\E/)) {
     my ($start, $end) = ($-[0], $+[0]);
 
-    while (my ($auto_generated_name, $desired_name) = each %{ $foreign_key_name_map{$table} || {} }) {
+    my %changes = map { %{$_} } grep { $_ } ($foreign_key_name_map{$domain}->{ALL}, $foreign_key_name_map{$domain}->{$table});
+    while (my ($auto_generated_name, $desired_name) = each %changes) {
       $foreign_key_definition =~ s/^ \s \s ${auto_generated_name} \b/  ${desired_name}/msx;
     }
 
       $foreign_key_definition =~ s/^ \s \s ${auto_generated_name} \b/  ${desired_name}/msx;
     }
 
@@ -185,7 +189,7 @@ CODE
     my $old_md5     = md5_hex($orig_file);
     my $new_size    = length $full_definition;
     my $new_md5     = md5_hex($full_definition);
     my $old_md5     = md5_hex($orig_file);
     my $new_size    = length $full_definition;
     my $new_md5     = md5_hex($full_definition);
-    if ($old_size == $new_size && $old_md5 == $new_md5) {
+    if ($old_size == $new_size && $old_md5 eq $new_md5) {
       notice("No changes in $meta_file, skipping.") unless $config{quiet};
       return;
     }
       notice("No changes in $meta_file, skipping.") unless $config{quiet};
       return;
     }
@@ -240,6 +244,7 @@ sub parse_args {
   GetOptions(
     'client=s'          => \ my $client,
     all                 => \ my $all,
   GetOptions(
     'client=s'          => \ my $client,
     all                 => \ my $all,
+    'db=s'              => \ my $db,
     'no-commit|dry-run' => \ my $nocommit,
     help                => sub { pod2usage(verbose => 99, sections => 'NAME|SYNOPSIS|OPTIONS') },
     quiet               => \ my $quiet,
     'no-commit|dry-run' => \ my $nocommit,
     help                => sub { pod2usage(verbose => 99, sections => 'NAME|SYNOPSIS|OPTIONS') },
     quiet               => \ my $quiet,
@@ -248,6 +253,7 @@ sub parse_args {
 
   $options->{client}   = $client;
   $options->{all}      = $all;
 
   $options->{client}   = $client;
   $options->{all}      = $all;
+  $options->{db}       = $db;
   $options->{nocommit} = $nocommit;
   $options->{quiet}    = $quiet;
   $options->{color}    = -t STDOUT ? 1 : 0;
   $options->{nocommit} = $nocommit;
   $options->{quiet}    = $quiet;
   $options->{color}    = -t STDOUT ? 1 : 0;
@@ -285,19 +291,32 @@ sub usage {
 }
 
 sub make_tables {
 }
 
 sub make_tables {
-  my @tables;
+  my %tables_by_domain;
   if ($config{all}) {
   if ($config{all}) {
-    my $db  = SL::DB::create(undef, 'KIVITENDO');
-    @tables = grep { my $table = $_; none { $_ eq $table } @{ $blacklist{KIVITENDO} } } $db->list_tables;
+    my @domains = $config{db} ? (uc $config{db}) : sort keys %package_names;
+
+    foreach my $domain (@domains) {
+      my $db  = SL::DB::create(undef, $domain);
+      $tables_by_domain{$domain} = [ grep { my $table = $_; none { $_ eq $table } @{ $blacklist{$domain} } } $db->list_tables ];
+      $db->disconnect;
+    }
 
   } elsif (@ARGV) {
 
   } elsif (@ARGV) {
-    @tables = @ARGV;
+    %tables_by_domain = partition_by {
+      my ($domain, $table) = split m{:};
+      $table ? uc($domain) : 'KIVITENDO';
+    } @ARGV;
+
+    foreach my $tables (values %tables_by_domain) {
+      s{.*:}{} for @{ $tables };
+    }
+
   } else {
     error("You specified neither --all nor any specific tables.");
     usage();
   }
 
   } else {
     error("You specified neither --all nor any specific tables.");
     usage();
   }
 
-  @tables;
+  return %tables_by_domain;
 }
 
 sub error {
 }
 
 sub error {
@@ -308,17 +327,32 @@ sub notice {
   print @_, $/;
 }
 
   print @_, $/;
 }
 
+sub check_errors_in_package_names {
+  foreach my $domain (sort keys %package_names) {
+    my @both = grep { $package_names{$domain}->{$_} } @{ $blacklist{$domain} || [] };
+    next unless @both;
+
+    print "Error: domain '$domain': The following table names are present in both the black list and the package name hash: ", join(' ', sort @both), "\n";
+    exit 1;
+  }
+}
+
 parse_args(\%config);
 setup();
 parse_args(\%config);
 setup();
-my @tables = make_tables();
+check_errors_in_package_names();
 
 
-my @unknown_tables = grep { !$package_names{KIVITENDO}->{$_} } @tables;
-if (@unknown_tables) {
-  error("The following tables do not have entries in \%SL::DB::Helper::Mappings::kivitendo_package_names: " . join(' ', sort @unknown_tables));
-  exit 1;
-}
+my %tables_by_domain = make_tables();
 
 
-process_table($_, $package_names{KIVITENDO}->{$_}) for @tables;
+foreach my $domain (keys %tables_by_domain) {
+  my @tables         = @{ $tables_by_domain{$domain} };
+  my @unknown_tables = grep { !$package_names{$domain}->{$_} } @tables;
+  if (@unknown_tables) {
+    error("The following tables do not have entries in \%SL::DB::Helper::Mappings::${domain}_package_names: " . join(' ', sort @unknown_tables));
+    exit 1;
+  }
+
+  process_table($domain, $_, $package_names{$domain}->{$_}) for @tables;
+}
 
 1;
 
 
 1;
 
@@ -332,20 +366,20 @@ rose_auto_create_model - mana Rose::DB::Object classes for kivitendo
 
 =head1 SYNOPSIS
 
 
 =head1 SYNOPSIS
 
-  scripts/rose_create_model.pl --client name-or-id table1 [table2 ...]
-  scripts/rose_create_model.pl --client name-or-id [--all|-a]
+  scripts/rose_auto_create_model.pl --client name-or-id [db1:]table1 [[db2:]table2 ...]
+  scripts/rose_auto_create_model.pl --client name-or-id [--all|-a]
 
   # updates all models
 
   # updates all models
-  scripts/rose_create_model.pl --client name-or-id --all
+  scripts/rose_auto_create_model.pl --client name-or-id --all [--db db]
 
   # updates only customer table, login taken from config
 
   # updates only customer table, login taken from config
-  scripts/rose_create_model.pl customer
+  scripts/rose_auto_create_model.pl customer
 
   # updates only parts table, package will be Part
 
   # updates only parts table, package will be Part
-  scripts/rose_create_model.pl parts=Part
+  scripts/rose_auto_create_model.pl parts=Part
 
   # try to update parts, but don't do it. tell what would happen in detail
 
   # try to update parts, but don't do it. tell what would happen in detail
-  scripts/rose_create_model.pl --no-commit parts
+  scripts/rose_auto_create_model.pl --no-commit parts
 
 =head1 DESCRIPTION
 
 
 =head1 DESCRIPTION
 
@@ -388,6 +422,17 @@ C<SL::DB::*> class file and a C<SL::DB::Manager::*> manager class
 file. The first one will be updated if the schema changes, the second
 and third ones will only be created if it they do not exist.
 
 file. The first one will be updated if the schema changes, the second
 and third ones will only be created if it they do not exist.
 
+=head1 DATABASE NAMES AND TABLES
+
+If you want to generate the data for specific tables only then you
+have to list them on the command line. The format is
+C<db-name:table-name>. The part C<db-name:> is optional and defaults
+to C<KIVITENDO:> – which means the tables in the default kivitendo
+database.
+
+Valid database names are keys in the hash returned by
+L<SL::DB::Helper::Mappings/get_package_names>.
+
 =head1 OPTIONS
 
 =over 4
 =head1 OPTIONS
 
 =over 4
@@ -405,6 +450,11 @@ Note that C<CLIENT> can be either a database ID or a client's name.
 Process all tables from the database. Only those that are blacklistes in
 L<SL::DB::Helper::Mappings> are excluded.
 
 Process all tables from the database. Only those that are blacklistes in
 L<SL::DB::Helper::Mappings> are excluded.
 
+=item C<--db db>
+
+In combination with C<--all> causes all tables in the specific
+database to be processed, not in all databases.
+
 =item C<--no-commit, -n>
 
 =item C<--dry-run>
 =item C<--no-commit, -n>
 
 =item C<--dry-run>