--- /dev/null
+package SL::DBUpgrade2;
+
+require Exporter;
+@ISA = qw(Exporter);
+
+@EXPORT = qw(parse_dbupdate_controls sort_dbupdate_controls);
+
+sub parse_dbupdate_controls {
+ $main::lxdebug->enter_sub();
+
+ my ($form, $dbdriver) = @_;
+
+ my $locale = $main::locale;
+
+ local *IN;
+ my %all_controls;
+
+ my $path = "sql/${dbdriver}-upgrade2";
+
+ foreach my $file_name (<$path/*.sql>, <$path/*.pl>) {
+ next unless (open(IN, $file_name));
+
+ my $file = $file_name;
+ $file =~ s|.*/||;
+
+ my $control = {
+ "priority" => 1000,
+ "depends" => [],
+ };
+
+ while (<IN>) {
+ chomp();
+ next unless (/^(--|\#)\s*\@/);
+ s/^(--|\#)\s*\@//;
+ s/\s*$//;
+ next if ($_ eq "");
+
+ my @fields = split(/\s*:\s*/, $_, 2);
+ next unless (scalar(@fields) == 2);
+
+ if ($fields[0] eq "depends") {
+ push(@{$control->{"depends"}}, split(/\s+/, $fields[1]));
+ } else {
+ $control->{$fields[0]} = $fields[1];
+ }
+ }
+
+ _control_error($form, $file_name,
+ $locale->text("Missing 'tag' field."))
+ unless ($control->{"tag"});
+
+ _control_error($form, $file_name,
+ $locale->text("The 'tag' field must only consist of " .
+ "alphanumeric characters or the carachters " .
+ "- _ ( )"))
+ if ($control->{"tag"} =~ /[^a-zA-Z0-9_\(\)\-]/);
+
+ _control_error($form, $file_name,
+ sprintf($locale->text("More than one control file " .
+ "with the tag '%s' exist."),
+ $control->{"tag"}))
+ if (defined($all_controls{$control->{"tag"}}));
+
+ _control_error($form, $file_name,
+ sprintf($locale->text("Missing 'description' field.")))
+ unless ($control->{"description"});
+
+ $control->{"priority"} *= 1;
+ $control->{"priority"} = 1000 unless ($control->{"priority"});
+
+ $control->{"file"} = $file;
+
+ map({ delete($control->{$_}); } qw(depth applied));
+
+ $all_controls{$control->{"tag"}} = $control;
+
+ close(IN);
+ }
+
+ foreach my $control (values(%all_controls)) {
+ foreach my $dependency (@{$control->{"depends"}}) {
+ _control_error($form, $control->{"file"},
+ sprintf($locale->text("Unknown dependency '%s'."),
+ $dependency))
+ if (!defined($all_controls{$dependency}));
+ }
+
+ map({ $_->{"loop"} = 0; } values(%all_controls));
+ _check_for_loops($form, $control->{"file"}, \%all_controls,
+ $control->{"tag"});
+ }
+
+ map({ _dbupdate2_calculate_depth(\%all_controls, $_->{"tag"}) }
+ values(%all_controls));
+
+ $main::lxdebug->leave_sub();
+
+ return \%all_controls;
+}
+
+sub _check_for_loops {
+ my ($form, $file_name, $controls, $tag, @path) = @_;
+
+ push(@path, $tag);
+
+ _control_error($form, $file_name,
+ $main::locale->text("Dependency loop detected:") .
+ " " . join(" -> ", @path))
+ if ($controls->{$tag}->{"loop"});
+
+ $controls->{$tag}->{"loop"} = 1;
+ map({ _check_for_loops($form, $file_name, $controls, $_, @path); }
+ @{$controls->{$tag}->{"depends"}});
+}
+
+sub _control_error {
+ my ($form, $file_name, $message) = @_;
+
+ my $form = $main::form;
+ my $locale = $main::locale;
+
+ $form->error(sprintf($locale->text("Error in database control file '%s': %s"),
+ $file_name, $message));
+}
+
+sub _dbupdate2_calculate_depth {
+ $main::lxdebug->enter_sub();
+
+ my ($tree, $tag) = @_;
+
+ my $node = $tree->{$tag};
+
+ return $main::lxdebug->leave_sub() if (defined($node->{"depth"}));
+
+ my $max_depth = 0;
+
+ foreach $tag (@{$node->{"depends"}}) {
+ _dbupdate2_calculate_depth($tree, $tag);
+ my $value = $tree->{$tag}->{"depth"};
+ $max_depth = $value if ($value > $max_depth);
+ }
+
+ $node->{"depth"} = $max_depth + 1;
+
+ $main::lxdebug->leave_sub();
+}
+
+sub sort_dbupdate_controls {
+ return
+ sort({ $a->{"depth"} != $b->{"depth"} ? $a->{"depth"} <=> $b->{"depth"} :
+ $a->{"priority"} != $b->{"priority"} ?
+ $a->{"priority"} <=> $b->{"priority"} :
+ $a->{"tag"} cmp $b->{"tag"} } values(%{$_[0]}));
+}
+
+
+1;
package User;
+use SL::DBUpgrade2;
+
sub new {
$main::lxdebug->enter_sub();
'$myconfig{tel}', 'user')|;
$dbh->do($query);
}
+
+ $self->create_schema_info_table($form, $dbh);
+
$dbh->disconnect;
$rc = 0;
- if (&update_available($myconfig{"dbdriver"}, $dbversion)) {
+ my $controls =
+ parse_dbupdate_controls($form, $myconfig{"dbdriver"});
- map { $form->{$_} = $myconfig{$_} }
- qw(dbname dbhost dbport dbdriver dbuser dbpasswd dbconnect);
+ map({ $form->{$_} = $myconfig{$_} }
+ qw(dbname dbhost dbport dbdriver dbuser dbpasswd dbconnect));
+
+ if (update_available($myconfig{"dbdriver"}, $dbversion) ||
+ update2_available($form, $controls)) {
$form->{"stylesheet"} = "lx-office-erp.css";
$form->{"title"} = $main::locale->text("Dataset upgrade");
}
# update the tables
- open FH, ">$userspath/nologin" or die "
-$!";
+ open(FH, ">$userspath/nologin") or die("$!");
# required for Oracle
$form->{dbdefault} = $sid;
$SIG{QUIT} = 'IGNORE';
$self->dbupdate($form);
+ $self->dbupdate2($form, $controls);
# remove lock file
- unlink "$userspath/nologin";
+ unlink("$userspath/nologin");
print($form->parse_html_template("dbupgrade/footer"));
sub process_query {
$main::lxdebug->enter_sub();
- my ($self, $form, $dbh, $filename, $version) = @_;
+ my ($self, $form, $dbh, $filename, $version_or_control) = @_;
# return unless (-f $filename);
}
}
- if ($version) {
+ if (ref($version_or_control) eq "HASH") {
+ $dbh->do("INSERT INTO schema_info (tag, login) VALUES (" .
+ $dbh->quote($version_or_control->{"tag"}) . ", " .
+ $dbh->quote($form->{"login"}) . ")");
+ } elsif ($version_or_control) {
$dbh->do("UPDATE defaults SET version = " . $dbh->quote($version));
}
$dbh->commit();
return ($#upgradescripts > -1);
}
+sub create_schema_info_table {
+ $main::lxdebug->enter_sub();
+
+ my ($self, $form, $dbh) = @_;
+
+ my $query = "SELECT tag FROM schema_info LIMIT 1";
+ if (!$dbh->do($query)) {
+ $query =
+ "CREATE TABLE schema_info (" .
+ " tag text, " .
+ " login text, " .
+ " itime timestamp DEFAULT now(), " .
+ " PRIMARY KEY (tag))";
+ $dbh->do($query) || $form->dberror($query);
+ }
+
+ $main::lxdebug->leave_sub();
+}
+
sub dbupdate {
$main::lxdebug->enter_sub();
last if ($version < $mindb);
# apply upgrade
- $main::lxdebug->message(DEBUG2, "Appliying Update $upgradescript");
+ $main::lxdebug->message(DEBUG2, "Applying Update $upgradescript");
if ($file_type eq "sql") {
$self->process_query($form, $dbh, "sql/" . $form->{"dbdriver"} . "-upgrade/$upgradescript", $str_maxdb);
} else {
return $rc;
}
+sub dbupdate2 {
+ $main::lxdebug->enter_sub();
+
+ my ($self, $form, $controls) = @_;
+
+ $form->{sid} = $form->{dbdefault};
+
+ my @upgradescripts = ();
+ my ($query, $sth, $tag);
+ my $rc = -2;
+
+ @upgradescripts = sort_dbupdate_controls($controls);
+
+ foreach my $db (split / /, $form->{dbupdate}) {
+
+ next unless $form->{$db};
+
+ # strip db from dataset
+ $db =~ s/^db//;
+ &dbconnect_vars($form, $db);
+
+ my $dbh =
+ DBI->connect($form->{dbconnect}, $form->{dbuser}, $form->{dbpasswd})
+ or $form->dberror;
+
+ map({ $_->{"applied"} = 0; } @upgradescripts);
+
+ $query = "SELECT tag FROM schema_info";
+ $sth = $dbh->prepare($query);
+ $sth->execute() || $form->dberror($query);
+ while (($tag) = $sth->fetchrow_array()) {
+ $controls->{$tag}->{"applied"} = 1 if (defined($controls->{$tag}));
+ }
+ $sth->finish();
+
+ my $all_applied = 1;
+ foreach (@upgradescripts) {
+ if (!$_->{"applied"}) {
+ $all_applied = 0;
+ last;
+ }
+ }
+
+ next if ($all_applied);
+
+ foreach my $control (@upgradescripts) {
+ next if ($control->{"applied"});
+
+ $control->{"file"} =~ /\.(sql|pl)$/;
+ my $file_type = $1;
+
+ # apply upgrade
+ $main::lxdebug->message(DEBUG2, "Applying Update $control->{file}");
+ print($form->parse_html_template("dbupgrade/upgrade_message2",
+ $control));
+
+ if ($file_type eq "sql") {
+ $self->process_query($form, $dbh, "sql/" . $form->{"dbdriver"} .
+ "-upgrade2/$control->{file}", $control);
+ } else {
+ $self->process_perl_script($form, $dbh, "sql/" . $form->{"dbdriver"} .
+ "-upgrade2/$control->{file}", $control);
+ }
+ }
+
+ $rc = 0;
+ $dbh->disconnect;
+
+ }
+
+ $main::lxdebug->leave_sub();
+
+ return $rc;
+}
+
+sub update2_available {
+ $main::lxdebug->enter_sub();
+
+ my ($form, $controls) = @_;
+
+ map({ $_->{"applied"} = 0; } values(%{$controls}));
+
+ dbconnect_vars($form, $form->{"dbname"});
+
+ my $dbh =
+ DBI->connect($form->{dbconnect}, $form->{dbuser}, $form->{dbpasswd}) ||
+ $form->dberror;
+
+ my ($query, $tag, $sth);
+
+ $query = "SELECT tag FROM schema_info";
+ $sth = $dbh->prepare($query);
+ $sth->execute() || $form->dberror($query);
+ while (($tag) = $sth->fetchrow_array()) {
+ $controls->{$tag}->{"applied"} = 1 if (defined($controls->{$tag}));
+ }
+ $sth->finish();
+ $dbh->disconnect();
+
+ map({ $main::lxdebug->leave_sub() and return 1 if (!$_->{"applied"}) }
+ values(%{$controls}));
+
+ $main::lxdebug->leave_sub();
+ return 0;
+}
+
sub create_config {
$main::lxdebug->enter_sub();
--- /dev/null
+Neuer Mechanismus für SQL-Upgradedateien
+----------------------------------------
+
+Der alte Mechanismus für SQL-Upgradescripte, der auf einer
+Versionsnummer beruht und dann in sql/Pg-upgrade nach einem Script für
+diese Versionsnummer sucht, schränkt sehr ein, z.B. was die parallele
+Entwicklung im stable- und unstable-Baum betrifft.
+
+Dieser Mechanismus wurde für Lx-Office 2.4.1 deutlich erweitert. Es
+werden weiterhin alle Scripte aus sql/Pg-upgrade
+ausgeführt. Zusätzlich gibt es aber ein zweites Verzeichnis,
+sql/Pg-upgrade2. In diesem Verzeichnis muss pro Datenbankupgrade eine
+Datei existieren, die neben den eigentlich auszuführenden SQL- oder
+Perl-Befehlen einige Kontrollinformationen enthält.
+
+Neu sind die Kontrollinformationen, die Abhängigkeiten und Prioritäten
+definieren können werden, sodass Datenbankscripte zwar in einer
+sicheren Reihenfolge ausgeführt werden (z.B. darf ein "ALTER TABLE"
+erst ausgeführt werden, wenn die Tabelle mit "CREATE TABLE" angelegt
+wurde), diese Reihenfolge aber so flexibel ist, dass man keine
+Versionsnummern mehr braucht.
+
+Lx-Office merkt sich dabei, welches der Upgradescripte in
+sql/Pg-upgrade2 bereits durchgeführt wurde und führt diese nicht
+erneut aus. Dazu dient die Tabelle "schema_info", die bei der
+Anmeldung automatisch angelegt wird.
+
+Format der Kontrollinformationen
+--------------------------------
+
+Die Kontrollinformationen sollten sich am Anfang der jeweiligen
+Upgradedatei befinden. Jede Zeile, die Kontrollinformationen enthält,
+hat dabei das folgende Format:
+
+Für SQL-Upgradedateien:
+
+-- @key: value
+
+
+Für Perl-Upgradedateien:
+
+# @key: value
+
+
+Leerzeichen vor "value" werden entfern.
+
+Die folgenden Schlüsselworte werden verarbeitet:
+
+* tag: Wird zwingend benötigt. Dies ist der "Name" des
+ Upgrades. Dieser "tag" kann von anderen Kontrolldateien in ihren
+ Abhängigkeiten verwendet werden (Schlüsselwort "depends"). Der "tag"
+ ist auch der Name, der in der Datenbank eingetragen wird.
+
+ Normalerweise sollte die Kontrolldatei genau so heißen wie der
+ "tag", nur mit der Endung ".sql" bzw. "pl".
+
+ Ein Tag darf nur aus alphanumerischen Zeichen sowie den Zeichen _ -
+ ( ) bestehen. Insbesondere sind Leerzeichen nicht erlaubt und
+ sollten stattdessen mit Unterstrichen ersetzt werden.
+
+* description: Benötigt. Eine Beschreibung, was in diesem Update
+ passiert. Diese wird dem Benutzer beim eigentlichen Datenbankupdate
+ angezeigt. Während der Tag in englisch gehalten sein sollte, sollte
+ die Beschreibung auf Deutsch erfolgen.
+
+* depends: Optional. Eine mit Leerzeichen getrennte Liste von "tags",
+ von denen dieses Upgradescript abhängt. Lx-Office stellt sicher,
+ dass die in dieser Liste aufgeführten Scripte bereits durchgeführt
+ wurden, bevor dieses Script ausgeführt wird.
+
+ Abhängigkeiten werden rekursiv betrachtet. Wenn also ein Script "b"
+ existiert, das von Änderungen in "a" abhängt, und eine neue
+ Kontrolldatei für "c" erstellt wird, die von Änderungen in "a" und
+ "b" abhängt, so genügt es, in "c" nur den Tag "b" als Abhängigkeit
+ zu definieren.
+
+ Es ist nicht erlaubt, sich selbst referenzierende Abhängigkeiten zu
+ definieren (z.B. "a" -> "b", "b" -> "c" und "c" -> "a").
+
+* priority: Optional. Ein Zahlenwert, der die Reihenfolge bestimmt, in
+ der Scripte ausgeführt werden, die die gleichen Abhängigkeitstiefen
+ besitzen. Fehlt dieser Parameter, so wird der Wert 1000 benutzt.
+
+ Dies ist reine Kosmetik. Für echte Reihenfolgen muss "depends"
+ benutzt werden. Lx-Office sortiert die auszuführenden Scripte zuerst
+ nach der Abhängigkeitstiefe (wenn "z" von "y" abhängt und "y" von
+ "x", so hat "z" eine Abhängigkeitstiefe von 2, "y" von 1 und "x" von
+ 0. "x" würde hier zuerst ausgeführt, dann "y", dann "z"), dann nach
+ der Priorität und bei gleicher Priorität alphabetisch nach dem
+ "tag".
+
+Hilfsscript dbupgrade2_tool.pl
+------------------------------
+
+Um die Arbeit mit den Abhängigkeiten etwas zu erleichtern, existiert
+ein Hilfsscript namens "scripts/dbupgrade2_tool.pl". Es muss aus dem
+Lx-Office-ERP-Basisverzeichnis heraus aufgerufen werden. Dieses Tool
+liest alle Datenbankupgradescripte aus dem Verzeichnis sql/Pg-upgrade2
+aus. Es benutzt dafür die gleichen Methoden wie Lx-Office selber,
+sodass alle Fehlersituationen von der Kommandozeile überprüft werden
+können.
+
+Wird dem Script kein weiterer Parameter übergeben, so wird nur eine
+Überprüfung der Felder und Abhängigkeiten vorgenommen. Man kann sich
+aber auch Informationen auf verschiedene Art ausgeben lassen:
+
+1. Listenform: "./scripts/dbupgrade2_tool.pl --list"
+
+ Gibt eine Liste aller Scripte aus. Die Liste ist in der Reihenfolge
+ sortiert, in der Lx-Office die Scripte ausführen würde. Es werden
+ neben der Listenposition der Tag, die Abhängigkeitstiefe und die
+ Priorität ausgegeben.
+
+2. Baumform: "./scripts/dbupgrade2_tool.pl --tree"
+
+ Listet alle Tags in Baumform basierend auf den Abhängigkeiten
+ auf. Die "Wurzelknoten" sind dabei die Scripte, von denen keine
+ anderen abhängen. Die Unterknoten sind Scripte, die beim
+ übergeordneten Script als Abhängigkeit eingetragen sind.
+
+3. Umgekehrte Baumform: "./scripts/dbupgrade2_tool.pl --rtree"
+
+ Listet alle Tags in Baumform basierend auf den Abhängigkeiten auf.
+ Die "Wurzelknoten" sind dabei die Scripte mit der geringsten
+ Abhängigkeitstiefe. Die Unterknoten sind Scripte, die das
+ übergeordnete Script als Abhängigkeit eingetragen haben.
+
+4. Baumform mit Postscriptausgabe: "./scripts/dbupgrade2_tool.pl --graphviz"
+
+ Benötigt das Tool "graphviz", um mit seiner Hilfe die Baumform aus
+ 3. in eine Postscriptdatei namens "db_dependencies.ps"
+ auszugeben. Dies ist vermutlich die übersichtlichste Form, weil
+ hierbei jeder Knoten nur einmal ausgegeben wird. Bei den
+ Textmodusbaumformen hingegen können Knoten und all ihre
+ Abhängigkeiten mehrfach ausgegeben werden.
+
+5. Scripte, von denen kein anderes Script abhängt:
+ "./scripts/dbupgrade2_tool.pl --nodeps"
+
+ Listet die Tags aller Scripte auf, von denen keine anderen Scripte
+ abhängen.
+
'Date Format' => 'Datumsformat',
'Delete' => 'Löschen',
'Delete Dataset' => 'Datenbank löschen',
+ 'Dependency loop detected:' => 'Schleife in den Abhängigkeiten entdeckt:',
'Directory' => 'Verzeichnis',
'Driver' => 'Treiber',
'Dropdown Limit' => 'Auswahllistenbegrenzung',
'E-mail' => 'eMail',
'Edit User' => 'Benutzerdaten bearbeiten',
+ 'Error in database control file \'%s\': %s' => 'Fehler in Datenbankupgradekontrolldatei \'%s\': %s',
'Existing Datasets' => 'existierende Datenbanken',
'Fax' => 'Fax',
'File locked!' => 'Datei gesperrt!',
'Login' => 'Anmeldung',
'Login name missing!' => 'Loginname fehlt.',
'Manager' => 'Manager',
+ 'Missing \'description\' field.' => 'Fehlendes Feld \'description\'.',
+ 'Missing \'tag\' field.' => 'Fehlendes Feld \'tag\'.',
+ 'More than one control file with the tag \'%s\' exist.' => 'Es gibt mehr als eine Kontrolldatei mit dem Tag \'%s\'.',
'Multibyte Encoding' => 'Schriftsatz',
'Name' => 'Name',
'New Templates' => 'neue Vorlagen',
'Stylesheet' => 'Stilvorlage',
'Supervisor' => 'Supervisor',
'Templates' => 'Vorlagen',
+ 'The \'tag\' field must only consist of alphanumeric characters or the carachters - _ ( )' => 'Das Feld \'tag\' darf nur aus alphanumerischen Zeichen und den Zeichen - _ ( ) bestehen.',
'The following Datasets are not in use and can be deleted' => 'Die folgenden Datenbanken sind nicht in Verwendung und können gelöscht werden',
'The following Datasets need to be updated' => 'Folgende Datenbanken müssen aktualisiert werden',
'The passwords do not match.' => 'Die Passwörter stimmen nicht überein.',
'This is a preliminary check for existing sources. Nothing will be created or deleted at this stage!' => 'In diesem Schritt werden bestehende Datenbanken gesucht. Es werden noch keine Änderungen vorgenommen!',
'To add a user to a group edit a name, change the login name and save. A new user with the same variables will then be saved under the new login name.' => 'Um einer Gruppe einen neuen Benutzer hinzuzufügen, ändern und speichern Sie am einfachsten einen bestehen den Zugriffsnamen. Unter dem neuen Namen wird dann ein Benutzer mit denselben Einstellungen angelegt.',
+ 'Unknown dependency \'%s\'.' => 'Unbekannte Abhängigkeit \'%s\'.',
'Unlock System' => 'System entsperren',
'Update Dataset' => 'Datenbank aktualisieren',
'Use Templates' => 'benutze Vorlagen',
'Ansprechpartner' => '',
'Application Error. No Format given!' => 'Fehler in der Anwendung. Das Format fehlt.',
'Application Error. Wrong Format: ' => 'Fehler in der Anwendung. Falsches Format: ',
+ 'Applying <TMPL_VAR file ESCAPE=HTML>:' => 'Führe <TMPL_VAR file ESCAPE=HTML> aus:',
'Apr' => 'Apr',
'April' => 'April',
'Are you sure you want to delete Invoice Number' => 'Soll die Rechnung mit folgender Nummer wirklich gelöscht werden:',
'Department deleted!' => 'Abteilung gelöscht.',
'Department saved!' => 'Abteilung gespeichert.',
'Departments' => 'Abteilungen',
+ 'Dependency loop detected:' => 'Schleife in den Abhängigkeiten entdeckt:',
'Deposit' => 'Gutschrift',
'Description' => 'Beschreibung',
'Description missing!' => 'Beschreibung fehlt.',
'Erlöse EU o. UStId' => 'Erlöse EU o. UStId',
'Erlöse Inland' => 'Erlöse Inland',
'Error' => 'Fehler',
+ 'Error in database control file \'%s\': %s' => 'Fehler in Datenbankupgradekontrolldatei \'%s\': %s',
'Error!' => 'Fehler!',
'Exch' => 'Wechselkurs.',
'Exchangerate' => 'Wechselkurs',
'Method' => 'Verfahren',
'Microfiche' => 'Mikrofilm',
'Minimum Amount' => 'Mindestbetrag',
+ 'Missing \'description\' field.' => 'Fehlendes Feld \'description\'.',
+ 'Missing \'tag\' field.' => 'Fehlendes Feld \'tag\'.',
'Missing Method!' => 'Fehlender Voranmeldungszeitraum',
'Missing Preferences: Outputroutine disabled' => 'Die Ausgabefunktionen sind wegen unzureichender Voreinstellungen deaktiviert!',
'Missing Tax Authoritys Preferences' => 'Fehlende Angaben zum Finanzamt!',
'Model' => 'Modell',
'Monat' => 'Monat',
'Monthly' => 'monatlich',
+ 'More than one control file with the tag \'%s\' exist.' => 'Es gibt mehr als eine Kontrolldatei mit dem Tag \'%s\'.',
'Multibyte Encoding' => 'Schriftsatz',
'MwSt. inkl.' => 'MwSt. inkl.',
'N/A' => 'N.Z.',
'Templates' => 'Vorlagen',
'Terms missing in row ' => '+Tage fehlen in Zeile ',
'Terms: Net' => 'Zahlungsziel',
+ 'The \'tag\' field must only consist of alphanumeric characters or the carachters - _ ( )' => 'Das Feld \'tag\' darf nur aus alphanumerischen Zeichen und den Zeichen - _ ( ) bestehen.',
'The base unit does not exist or it is about to be deleted in row %d.' => 'Die Basiseinheit in Zeile %d existiert nicht oder soll gelöscht werden.',
'The base unit does not exist.' => 'Die Basiseinheit existiert nicht.',
'The base unit relations must not contain loops (e.g. by saying that unit A\'s base unit is B, B\'s base unit is C and C\'s base unit is A) in row %d.' => 'Die Beziehungen der Einheiten dürfen keine Schleifen beinhalten (z.B. wenn gesagt wird, dass Einheit As Basiseinheit B, Bs Basiseinheit C und Cs Basiseinheit A ist) in Zeile %d.',
'Unit' => 'Einheit',
'Unit of measure' => 'Maßeinheit',
'Units' => 'Einheiten',
+ 'Unknown dependency \'%s\'.' => 'Unbekannte Abhängigkeit \'%s\'.',
'Unlock System' => 'System entsperren',
'Until' => 'Bis',
'Update' => 'Erneuern',
'Department deleted!' => 'Abteilung gelöscht.',
'Department saved!' => 'Abteilung gespeichert.',
'Departments' => 'Abteilungen',
+ 'Dependency loop detected:' => 'Schleife in den Abhängigkeiten entdeckt:',
'Description' => 'Beschreibung',
'Description missing!' => 'Beschreibung fehlt.',
'Discount' => 'Rabatt',
'Erlöse EU m. UStId' => 'Erlöse EU m. UStId',
'Erlöse EU o. UStId' => 'Erlöse EU o. UStId',
'Erlöse Inland' => 'Erlöse Inland',
+ 'Error in database control file \'%s\': %s' => 'Fehler in Datenbankupgradekontrolldatei \'%s\': %s',
'Expense' => 'Aufwandskonto',
'Expense Account' => 'Aufwandskonto',
'Expense/Asset' => 'Aufwand/Anlagen',
'Link' => 'Verknüpfungen',
'Long Dates' => 'Lange Monatsnamen',
'Long Description' => 'Langtext',
+ 'Missing \'description\' field.' => 'Fehlendes Feld \'description\'.',
+ 'Missing \'tag\' field.' => 'Fehlendes Feld \'tag\'.',
+ 'More than one control file with the tag \'%s\' exist.' => 'Es gibt mehr als eine Kontrolldatei mit dem Tag \'%s\'.',
'Name' => 'Name',
'Netto Terms' => 'Zahlungsziel netto',
'No' => 'Nein',
'Template Code' => 'Vorlagenkürzel',
'Template Code missing!' => 'Vorlagenkürzel fehlt!',
'Template saved!' => 'Schablone gespeichert!',
+ 'The \'tag\' field must only consist of alphanumeric characters or the carachters - _ ( )' => 'Das Feld \'tag\' darf nur aus alphanumerischen Zeichen und den Zeichen - _ ( ) bestehen.',
'The base unit does not exist or it is about to be deleted in row %d.' => 'Die Basiseinheit in Zeile %d existiert nicht oder soll gelöscht werden.',
'The base unit does not exist.' => 'Die Basiseinheit existiert nicht.',
'The base unit relations must not contain loops (e.g. by saying that unit A\'s base unit is B, B\'s base unit is C and C\'s base unit is A) in row %d.' => 'Die Beziehungen der Einheiten dürfen keine Schleifen beinhalten (z.B. wenn gesagt wird, dass Einheit As Basiseinheit B, Bs Basiseinheit C und Cs Basiseinheit A ist) in Zeile %d.',
'UStVA-Nr. 98' => 'Kz. 98',
'Umsatzsteuervoranmeldung' => 'Umsatzsteuervoranmeldung',
'Unit' => 'Einheit',
+ 'Unknown dependency \'%s\'.' => 'Unbekannte Abhängigkeit \'%s\'.',
'Value' => 'Wert',
'Variable' => 'Variable',
'Warehouse deleted!' => 'Das Lager wurde gelöscht.',
$basedir = "../..";
$bindir = "$basedir/bin/mozilla";
$dbupdir = "$basedir/sql/Pg-upgrade";
+$dbupdir2 = "$basedir/sql/Pg-upgrade2";
$menufile = "menu.ini";
$submitsearch = qr/type\s*=\s*[\"\']?submit/i;
@dbplfiles = grep { /\.pl$/ } readdir DIR;
closedir DIR;
+opendir DIR, $dbupdir2 or die "$!";
+@dbplfiles2 = grep { /\.pl$/ } readdir DIR;
+closedir DIR;
+
# slurp the translations in
if (-f 'all') {
require "all";
map({ handle_file($_, $bindir); } @progfiles);
map({ handle_file($_, $dbupdir); } @dbplfiles);
+map({ handle_file($_, $dbupdir2); } @dbplfiles2);
sub handle_file {
my ($file, $dir) = @_;
'Database Host' => 'Datenbankcomputer',
'Dataset' => 'Datenbank',
'Dataset upgrade' => 'Datenbankaktualisierung',
+ 'Dependency loop detected:' => 'Schleife in den Abhängigkeiten entdeckt:',
+ 'Error in database control file \'%s\': %s' => 'Fehler in Datenbankupgradekontrolldatei \'%s\': %s',
'Incorrect username or password!' => 'Ungültiger Benutzername oder falsches Passwort!',
'Licensed to' => 'Lizensiert für',
'Login' => 'Anmeldung',
'Login Name' => 'Benutzername',
+ 'Missing \'description\' field.' => 'Fehlendes Feld \'description\'.',
+ 'Missing \'tag\' field.' => 'Fehlendes Feld \'tag\'.',
+ 'More than one control file with the tag \'%s\' exist.' => 'Es gibt mehr als eine Kontrolldatei mit dem Tag \'%s\'.',
'Password' => 'Passwort',
+ 'The \'tag\' field must only consist of alphanumeric characters or the carachters - _ ( )' => 'Das Feld \'tag\' darf nur aus alphanumerischen Zeichen und den Zeichen - _ ( ) bestehen.',
+ 'Unknown dependency \'%s\'.' => 'Unbekannte Abhängigkeit \'%s\'.',
'User' => 'Benutzer',
'Version' => 'Version',
'You are logged out!' => 'Auf Wiedersehen!',
'Dauerfristverlängerung' => 'Dauerfristverlängerung',
'Dec' => 'Dez',
'December' => 'Dezember',
+ 'Dependency loop detected:' => 'Schleife in den Abhängigkeiten entdeckt:',
'Description' => 'Beschreibung',
'ELSTER Export nach Taxbird' => 'ELSTER-Export nach Taxbird',
'ELSTER Export nach Winston' => 'ELSTER Export nach Winston',
'ELSTER-Steuernummer: ' => 'ELSTER-Steuernummer: ',
+ 'Error in database control file \'%s\': %s' => 'Fehler in Datenbankupgradekontrolldatei \'%s\': %s',
'Fax' => 'Fax',
'Fax. : ' => 'Fax. : ',
'Fax.: ' => 'Fax.: ',
'May' => 'Mai',
'May ' => 'Mai',
'Method' => 'Verfahren',
+ 'Missing \'description\' field.' => 'Fehlendes Feld \'description\'.',
+ 'Missing \'tag\' field.' => 'Fehlendes Feld \'tag\'.',
'Missing Method!' => 'Fehlender Voranmeldungszeitraum',
'Missing Preferences: Outputroutine disabled' => 'Die Ausgabefunktionen sind wegen unzureichender Voreinstellungen deaktiviert!',
'Missing Tax Authoritys Preferences' => 'Fehlende Angaben zum Finanzamt!',
+ 'More than one control file with the tag \'%s\' exist.' => 'Es gibt mehr als eine Kontrolldatei mit dem Tag \'%s\'.',
'Name' => 'Name',
'Nov' => 'Nov',
'November' => 'November',
'Tel. : ' => 'Tel. : ',
'Tel.: ' => 'Tel.: ',
'Telefon' => 'Telefon',
+ 'The \'tag\' field must only consist of alphanumeric characters or the carachters - _ ( )' => 'Das Feld \'tag\' darf nur aus alphanumerischen Zeichen und den Zeichen - _ ( ) bestehen.',
'USTVA-Hint: Method' => 'Wenn Sie Ist-Versteuert sind, wählen Sie die Einnahmen-/Überschuß-Rechnung aus. Sind Sie Soll-Versteuert und bilanzverpflichtet, dann wählen Sie Bilanz aus.',
'USTVA-Hint: Tax Authoritys' => 'Bitte das Bundesland UND die Stadt bzw. den Einzugsbereich Ihres zuständigen Finanzamts auswählen.',
'UStVA' => 'UStVA',
'UStVA als PDF-Dokument' => 'UStVa als PDF-Dokument',
+ 'Unknown dependency \'%s\'.' => 'Unbekannte Abhängigkeit \'%s\'.',
'Vendor not on file!' => 'Lieferant ist nicht in der Datenbank!',
'Verfahren' => 'Verfahren',
'Verrechnung des Erstattungsbetrages erwünscht (Zeile 71)' => 'Verrechnung des Erstattungsbetrages erwünscht (Zeile 71)',
--- /dev/null
+#!/usr/bin/perl
+
+BEGIN {
+ if (! -d "bin" || ! -d "SL") {
+ print("This tool must be run from the Lx-Office ERP base directory.\n");
+ exit(1);
+ }
+}
+
+use DBI;
+use Data::Dumper;
+use Getopt::Long;
+
+use SL::LXDebug;
+
+$lxdebug = LXDebug->new();
+
+use SL::Form;
+use SL::DBUpgrade2;
+
+#######
+#######
+#######
+
+sub show_help {
+ print("dbupgrade2_tool.pl [--list] [--tree] [--rtree] [--graphviz]\n" .
+ " [--nodepds] [--help]\n");
+}
+
+sub calc_rev_depends {
+ map({ $_->{"rev_depends"} = []; } values(%{$controls}));
+ foreach my $control (values(%{$controls})) {
+ map({ push(@{$controls->{$_}{"rev_depends"}}, $control->{"tag"}) }
+ @{$control->{"depends"}});
+ }
+}
+
+sub dump_list {
+ my @sorted_controls = sort_dbupdate_controls($controls);
+
+ print("LIST VIEW\n\n");
+ print("number tag depth priority\n");
+ $i = 0;
+ foreach (@sorted_controls) {
+ print("$i $_->{tag} $_->{depth} $_->{priority}\n");
+ $i++;
+ }
+
+ print("\n");
+}
+
+sub dump_node {
+ my ($tag, $depth) = @_;
+
+ print(" " x $depth . $tag . "\n");
+
+ my $c = $controls->{$tag};
+ my $num = scalar(@{$c->{"depends"}});
+ for (my $i = 0; $i < $num; $i++) {
+ dump_node($c->{"depends"}[$i], $depth + 1);
+ }
+}
+
+sub dump_tree {
+ print("TREE VIEW\n\n");
+
+ calc_rev_depends();
+
+ my @sorted_controls = sort_dbupdate_controls($controls);
+
+ foreach my $control (@sorted_controls) {
+ dump_node($control->{"tag"}, "") unless (@{$control->{"rev_depends"}});
+ }
+
+ print("\n");
+}
+
+sub dump_node_reverse {
+ my ($tag, $depth) = @_;
+
+ print(" " x $depth . $tag . "\n");
+
+ my $c = $controls->{$tag};
+ my $num = scalar(@{$c->{"rev_depends"}});
+ for (my $i = 0; $i < $num; $i++) {
+ dump_node_reverse($c->{"rev_depends"}[$i], $depth + 1);
+ }
+}
+
+sub dump_tree_reverse {
+ print("REVERSE TREE VIEW\n\n");
+
+ calc_rev_depends();
+
+ my @sorted_controls = sort_dbupdate_controls($controls);
+
+ foreach my $control (@sorted_controls) {
+ last if ($control->{"depth"} > 1);
+ dump_node_reverse($control->{"tag"}, "");
+ }
+
+ print("\n");
+}
+
+sub dump_graphviz {
+ print("GRAPHVIZ POSTCRIPT\n\n");
+ print("Output will be written to db_dependencies.ps\n");
+ $dot = "|dot -Tps ";
+ open(OUT, "${dot}> db_dependencies.ps");
+ print(OUT
+ "digraph db_dependencies {\n" .
+ "node [shape=box];\n");
+ my %ranks;
+ foreach my $c (values(%{$controls})) {
+ $ranks{$c->{"depth"}} = [] unless ($ranks{$c->{"depth"}});
+ push(@{$ranks{$c->{"depth"}}}, $c->{"tag"});
+ }
+ foreach (sort(keys(%ranks))) {
+ print(OUT "{ rank = same; " .
+ join("", map({ '"' . $_ . '"; ' } @{$ranks{$_}})) .
+ " }\n");
+ }
+ foreach my $c (values(%{$controls})) {
+ print(OUT "$c->{tag};\n");
+ foreach my $d (@{$c->{"depends"}}) {
+ print(OUT "$c->{tag} -> $d;\n");
+ }
+ }
+ print(OUT "}\n");
+ close(OUT);
+}
+
+sub dump_nodeps {
+ calc_rev_depends();
+
+ print("SCRIPTS NO OTHER SCRIPTS DEPEND ON\n\n" .
+ join("\n",
+ map({ $_->{"tag"} }
+ grep({ !@{$_->{"rev_depends"}} }
+ values(%{$controls})))) .
+ "\n\n");
+}
+
+#######
+#######
+#######
+
+eval { require "lx-erp.conf"; };
+
+$form = Form->new();
+$locale = Locale->new("de", "login");
+
+#######
+#######
+#######
+
+my ($opt_list, $opt_tree, $opt_rtree, $opt_nodeps, $opt_graphviz, $opt_help);
+
+GetOptions("list" => \$opt_list,
+ "tree" => \$opt_tree,
+ "rtree" => \$opt_rtree,
+ "nodeps" => \$opt_nodeps,
+ "graphviz" => \$opt_graphviz,
+ "help" => \$opt_help,
+ );
+
+if ($opt_help) {
+ show_help();
+ exit(0);
+}
+
+$controls = parse_dbupdate_controls($form, "Pg");
+
+if ($opt_list) {
+ dump_list();
+}
+
+if ($opt_tree) {
+ dump_tree();
+}
+
+if ($opt_rtree) {
+ dump_tree_reverse();
+}
+
+if ($opt_graphviz) {
+ dump_graphviz();
+}
+
+if ($opt_nodeps) {
+ dump_nodeps();
+}
+++ /dev/null
-ALTER TABLE language ADD COLUMN output_numberformat text;
-ALTER TABLE language ADD COLUMN output_dateformat text;
-ALTER TABLE language ADD COLUMN output_longdates boolean;
+++ /dev/null
-CREATE TABLE units_language (
- unit varchar (20) NOT NULL,
- language_id integer NOT NULL,
- localized varchar (20),
- localized_plural varchar (20),
-
- FOREIGN KEY (unit) REFERENCES units (name),
- FOREIGN KEY (language_id) REFERENCES language (id)
-);
-CREATE INDEX units_name_idx ON units (name);
-CREATE INDEX units_language_unit_idx ON units_language (unit);
+++ /dev/null
-update tax set id=0 WHERE taxkey=0;
--- /dev/null
+Bitte lesen Sie die Datei doc/sql-upgrade-dateien.txt, bevor
+Sie hier Dateien anlegen.
+
--- /dev/null
+-- @tag: language_output_formatting
+-- @description: Speichern des Ausgabeformates für Zahlen und Datumsangaben bei jeder Sprache.
+-- @depends:
+ALTER TABLE language ADD COLUMN output_numberformat text;
+ALTER TABLE language ADD COLUMN output_dateformat text;
+ALTER TABLE language ADD COLUMN output_longdates boolean;
--- /dev/null
+-- @tag: tax_id_if_taxkey_is_0
+-- @description: Aktualisierung der Spalte tax.id, wenn tax.taxkey = 0 ist.
+-- @depends:
+UPDATE tax SET id = 0 WHERE taxkey = 0;
--- /dev/null
+-- @tag: units_translations_and_singular_plural_distinction
+-- @description: Für jede Einheit kann für jede Sprache eine Übersetzung sowie eine Unterscheidung zwischen Singular und Plural gespeichert werden.
+-- @depends:
+CREATE TABLE units_language (
+ unit varchar (20) NOT NULL,
+ language_id integer NOT NULL,
+ localized varchar (20),
+ localized_plural varchar (20),
+
+ FOREIGN KEY (unit) REFERENCES units (name),
+ FOREIGN KEY (language_id) REFERENCES language (id)
+);
+CREATE INDEX units_name_idx ON units (name);
+CREATE INDEX units_language_unit_idx ON units_language (unit);
--- /dev/null
+Führe <TMPL_VAR file ESCAPE=HTML> aus: <TMPL_VAR description ESCAPE=HTML><br>
--- /dev/null
+<translate>Applying <TMPL_VAR file ESCAPE=HTML>:</translate> <TMPL_VAR description ESCAPE=HTML><br>