Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 3277b6bd

Von Moritz Bunkus vor mehr als 11 Jahren hinzugefügt

  • ID 3277b6bd27980f88d43fb4ff95b76d250aaec195
  • Vorgänger 0b1904c3
  • Nachfolger 3ced230d

Datenbankupgradescript für Mandanten

Unterschiede anzeigen:

locale/de/all
179 179
  'All changes in that file have been reverted.' => 'Alle Änderungen in dieser Datei wurden rückgängig gemacht.',
180 180
  'All database upgrades have been applied.' => 'Alle Datenbankupdates wurden eingespielt.',
181 181
  'All general ledger entries'  => 'Alle Hauptbucheinträge',
182
  'All groups'                  => 'Alle Gruppen',
182 183
  'All of the exports you have selected were already closed.' => 'Alle von Ihnen ausgewählten Exporte sind bereits abgeschlossen.',
183 184
  'All reports'                 => 'Alle Berichte (Kontenübersicht, Summen- u. Saldenliste, GuV, BWA, Bilanz, Projektbuchungen)',
184 185
  'All the selected exports have already been closed, or all of their items have already been executed.' => 'Alle ausgewählten Exporte sind als abgeschlossen markiert, oder für alle Einträge wurden bereits Zahlungen verbucht.',
......
435 436
  'Cleared Balance'             => 'abgeschlossen',
436 437
  'Clearing Tax Received (No 71)' => 'Verrechnung des Erstattungsbetrages erwünscht (Zeile 71)',
437 438
  'Click on login name to edit!' => 'Zum Bearbeiten den Benutzernamen anklicken!',
439
  'Client #1'                   => 'Mandant #1',
438 440
  'Client Configuration'        => 'Mandantenkonfiguration',
439 441
  'Client Configuration saved!' => 'Mandantenkonfiguration gespeichert!',
442
  'Client name'                 => 'Mandantenname',
440 443
  'Close'                       => 'Übernehmen',
441 444
  'Close Books up to'           => 'Die Bücher abschließen bis zum',
442 445
  'Close Flash'                 => 'Schließen',
......
450 453
  'Comment'                     => 'Kommentar',
451 454
  'Company'                     => 'Firma',
452 455
  'Company Name'                => 'Firmenname',
456
  'Company name'                => 'Firmenname',
453 457
  'Compare to'                  => 'Gegenüberstellen zu',
454 458
  'Configuration'               => 'Einstellungen',
455 459
  'Configuration of individual TODO items' => 'Konfiguration für die einzelnen Aufgabenlistenpunkte',
......
512 516
  'Create new'                  => 'Neu erfassen',
513 517
  'Create new background job'   => 'Neuen Hintergrund-Job anlegen',
514 518
  'Create new business'         => 'Kunden-/Lieferantentyp erfassen',
519
  'Create new client #1'        => 'Neuen Mandanten #1 anlegen',
515 520
  'Create new department'       => 'Neue Abteilung erfassen',
516 521
  'Create new payment term'     => 'Neue Zahlungsbedingung anlegen',
517 522
  'Create tables'               => 'Tabellen anlegen',
......
587 592
  'Database User missing!'      => 'Datenbankbenutzer fehlt!',
588 593
  'Database backups and restorations are disabled in the configuration.' => 'Datenbanksicherungen und -wiederherstellungen sind in der Konfiguration deaktiviert.',
589 594
  'Database name'               => 'Datenbankname',
595
  'Database settings'           => 'Datenbankeinstellungen',
590 596
  'Database template'           => 'Datenbankvorlage',
591 597
  'Database update error:'      => 'Fehler beim Datenbankupgrade:',
592 598
  'Dataset'                     => 'Datenbank',
......
841 847
  'Error in position #1: You must either assign no transfer at all or the full quantity of #2 #3.' => 'Fehler in Position #1: Sie müssen einer Position entweder gar keinen Lagerausgang oder die vollständige im Lieferschein vermerkte Menge von #2 #3 zuweisen.',
842 848
  'Error in row #1: The quantity you entered is bigger than the stocked quantity.' => 'Fehler in Zeile #1: Die angegebene Menge ist größer als die vorhandene Menge.',
843 849
  'Error message from the database driver:' => 'Fehlermeldung des Datenbanktreibers:',
850
  'Error message from the database: #1' => 'Fehlermeldung der Datenbank: #1',
844 851
  'Error when saving: #1'       => 'Fehler beim Speichern: #1',
845 852
  'Error!'                      => 'Fehler!',
846 853
  'Error: Buchungsgruppe missing or invalid' => 'Fehler: Buchungsgruppe fehlt oder ungültig',
......
969 976
  'General Ledger Transaction'  => 'Dialogbuchung',
970 977
  'General ledger and cash'     => 'Finanzbuchhaltung und Zahlungsverkehr',
971 978
  'General ledger corrections'  => 'Korrekturen im Hauptbuch',
979
  'General settings'            => 'Allgemeine Einstellungen',
972 980
  'Generic Tax Report'          => 'USTVA Bericht',
973 981
  'Git revision: #1, #2 #3'     => 'Git-Revision: #1, #2 #3',
974 982
  'Given Name'                  => 'Vorname',
......
984 992
  'Group missing!'              => 'Warengruppe fehlt!',
985 993
  'Group saved!'                => 'Warengruppe gespeichert!',
986 994
  'Groups'                      => 'Warengruppen',
995
  'Groups that are valid for this client for access rights' => 'Gruppen, die für diesen Mandanten gültig sind',
996
  'Groups valid for this client' => 'Für Mandanten gültige Gruppen',
987 997
  'HTML'                        => 'HTML',
988 998
  'HTML Templates'              => 'HTML-Vorlagen',
989 999
  'Hardcopy'                    => 'Seite drucken',
......
1069 1079
  'International'               => 'Ausland',
1070 1080
  'Internet'                    => 'Internet',
1071 1081
  'Introduction of Buchungsgruppen' => 'Einführung von Buchungsgruppen',
1082
  'Introduction of clients'     => 'Einführung von Mandanten',
1072 1083
  'Introduction of units'       => 'Einführung von Einheiten',
1073 1084
  'Inv. Duedate'                => 'Rg. Fälligkeit',
1074 1085
  'Invalid'                     => 'Ungültig',
......
1104 1115
  'Invoices, Credit Notes & AR Transactions' => 'Rechnungen, Gutschriften & Debitorenbuchungen',
1105 1116
  'Is Searchable'               => 'Durchsuchbar',
1106 1117
  'Is this a summary account to record' => 'Sammelkonto für',
1118
  'It can be changed later but must be unique within the installation.' => 'Er ist nachträglich änderbar, muss aber im System eindeutig sein.',
1107 1119
  'It is not allowed that a summary account occurs in a drop-down menu!' => 'Ein Sammelkonto darf nicht in Aufklappmenüs aufgenommen werden!',
1108 1120
  'It is possible that even after such a correction there is something wrong with this transaction (e.g. taxes that don\'t match the selected taxkey). Therefore you should re-run the general ledger analysis.' => 'Auch nach einer Korrektur kann es mit dieser Buchung noch weitere Probleme geben (z.B. nicht zum Steuerschlüssel passende Steuern), weshalb ein erneutes Ausführen der Hauptbuchanalyse empfohlen wird.',
1109 1121
  'It is possible to do this automatically for some Buchungsgruppen, but not for all.' => 'Es ist möglich, dies für einige, aber nicht für alle Buchungsgruppen automatisch zu erledigen.',
......
1278 1290
  'New Templates'               => 'Erzeuge Vorlagen, Name',
1279 1291
  'New assembly'                => 'Neues Erzeugnis',
1280 1292
  'New bank account'            => 'Neues Bankkonto',
1293
  'New client #1: The database configuration fields "host", "port", "name" and "user" must not be empty.' => 'Neuer Mandant #1: Die Datenbankkonfigurationsfelder "Host", "Port" und "Name" dürfen nicht leer sein.',
1294
  'New client #1: The name must be unique and not empty.' => 'Neuer Mandant #1: Der Name darf nicht leer und muss eindeutig sein.',
1281 1295
  'New contact'                 => 'Neue Ansprechperson',
1282 1296
  'New customer'                => 'Neuer Kunde',
1283 1297
  'New filter for tax accounts' => 'Neue Filter für Steuerkonten',
......
1375 1389
  'One or more Perl modules missing' => 'Ein oder mehr Perl-Module fehlen',
1376 1390
  'Only Warnings and Errors'    => 'Nur Warnungen und Fehler',
1377 1391
  'Only due follow-ups'         => 'Nur fällige Wiedervorlagen',
1392
  'Only groups that have been configured for the client the user logs in to will be considered.' => 'Allerdings werden nur diejenigen Gruppen herangezogen, die für den Mandanten konfiguriert sind.',
1378 1393
  'Only shown in item mode'     => 'werden nur im Artikelmodus angezeigt',
1379 1394
  'Oops. No valid action found to dispatch. Please report this case to the kivitendo team.' => 'Ups. Es wurde keine gültige Funktion zum Aufrufen gefunden. Bitte berichten Sie diesen Fall den kivitendo-Entwicklern.',
1380 1395
  'Open'                        => 'Offen',
......
1480 1495
  'Please choose for which categories the taxes should be displayed (otherwise remove the ticks):' => 'Bitte wählen Sie für welche Kontoart die Steuer angezeigt werden soll (ansonsten einfach die Häkchen entfernen)',
1481 1496
  'Please contact your administrator or a service provider.' => 'Bitte kontaktieren Sie Ihren Administrator oder einen Dienstleister.',
1482 1497
  'Please contact your administrator.' => 'Bitte wenden Sie sich an Ihren Administrator.',
1498
  'Please correct the settings and try again or deactivate that client.' => 'Bitte korrigieren Sie die Einstellungen und versuchen Sie es erneut, oder deaktivieren Sie diesen Mandanten.',
1483 1499
  'Please define a taxkey for the following taxes and run the update again:' => 'Bitte definieren Sie einen Steuerschlüssel für die folgenden Steuern und starten Sie dann das Update erneut:',
1484 1500
  'Please enter a profile name.' => 'Bitte geben Sie einen Profilnamen an.',
1485 1501
  'Please enter the currency you are working with.' => 'Bitte geben Sie die Währung an, mit der Sie arbeiten.',
......
1504 1520
  'Please select the database you want to backup' => 'Bitte wählen Sie die zu sichernde Datenbank gefunden',
1505 1521
  'Please select the destination bank account for the collections:' => 'Bitte wählen Sie das Bankkonto als Ziel für die Einzüge aus:',
1506 1522
  'Please select the source bank account for the transfers:' => 'Bitte wählen Sie das Bankkonto als Quelle für die Überweisungen aus:',
1523
  'Please select which client configurations you want to create.' => 'Bitte wählen Sie aus, welche Mandanten mit welchen Einstellungen angelegt werden sollen.',
1507 1524
  'Please seletct the dataset you want to delete:' => 'Bitte wählen Sie die zu löschende Datenbank aus:',
1508 1525
  'Please set another taxnumber for the following taxes and run the update again:' => 'Bitte wählen Sie ein anderes Steuerautomatik-Konto für die folgenden Steuern aus uns starten Sie dann das Update erneut.',
1509 1526
  'Please specify a description for the warehouse designated for these goods.' => 'Bitte geben Sie den Namen des Ziellagers für die übernommenen Daten ein.',
......
1547 1564
  'Print dunnings'              => 'Mahnungen drucken',
1548 1565
  'Print list'                  => 'Liste ausdrucken',
1549 1566
  'Print options'               => 'Druckoptionen',
1567
  'Print templates'             => 'Druckvorlagen',
1550 1568
  'Printer'                     => 'Drucker',
1551 1569
  'Printer Command'             => 'Druckbefehl',
1552 1570
  'Printer Command missing!'    => 'Druckbefehl fehlt',
......
1960 1978
  'Text, text field and number variables: The default value will be used as-is.' => 'Textzeilen, Textfelder und Zahlenvariablen: Der Standardwert wird so wie er ist übernommen.',
1961 1979
  'That export does not exist.' => 'Dieser Export existiert nicht.',
1962 1980
  'That is why kivitendo could not find a default currency.' => 'Daher konnte kivitendo keine Standardwährung finden.',
1981
  'The \'name\' is the field shown to the user during login.' => 'Der \'Name\' ist derjenige, der dem Benutzer beim Login angezeigt wird.',
1963 1982
  'The \'tag\' field must only consist of alphanumeric characters or the carachters - _ ( )' => 'Das Feld \'tag\' darf nur aus alphanumerischen Zeichen und den Zeichen - _ ( ) bestehen.',
1964 1983
  'The AP transaction #1 has been deleted.' => 'Die Kreditorenbuchung #1 wurde gelöscht.',
1965 1984
  'The AR transaction #1 has been deleted.' => 'Die Debitorenbuchung #1 wurde gelöscht.',
......
1969 1988
  'The LDAP server "#1:#2" is unreachable. Please check config/kivitendo.conf.' => 'Der LDAP-Server "#1:#2" ist nicht erreichbar. Bitte überprüfen Sie die Angaben in config/kivitendo.conf.',
1970 1989
  'The SEPA export has been created.' => 'Der SEPA-Export wurde erstellt',
1971 1990
  'The SEPA strings have been saved.' => 'Die bei SEPA-Überweisungen verwendeten Begriffe wurden gespeichert.',
1991
  'The access rights a user has within a client instance is still governed by his group membership.' => 'Welche Zugriffsrechte ein Benutzer innerhalb eines Mandanten hat, wird weiterhin über Gruppenmitgliedschaften geregelt.',
1972 1992
  'The access rights have been saved.' => 'Die Zugriffsrechte wurden gespeichert.',
1973 1993
  'The account 3804 already exists, the update will be skipped.' => 'Das Konto 3804 existiert schon, das Update wird übersprungen.',
1974 1994
  'The account 3804 will not be added automatically.' => 'Das Konto 3804 wird nicht automatisch hinzugefügt.',
......
1998 2018
  'The columns "Dunning Duedate", "Total Fees" and "Interest" show data for the previous dunning created for this invoice.' => 'Die Spalten "Zahlbar bis", "Kumulierte Gebühren" und "Zinsen" zeigen Daten der letzten für diese Rechnung erzeugten Mahnung.',
1999 2019
  'The connection to the LDAP server cannot be encrypted (SSL/TLS startup failure). Please check config/kivitendo.conf.' => 'Die Verbindung zum LDAP-Server kann nicht verschlüsselt werden (Fehler bei SSL/TLS-Initialisierung). Bitte überprüfen Sie die Angaben in config/kivitendo.conf.',
2000 2020
  'The connection to the authentication database failed:' => 'Die Verbindung zur Authentifizierungsdatenbank schlug fehl:',
2021
  'The connection to the configured client database "#1" on host "#2:#3" failed.' => 'Die Verbindung zur konfigurierten Datenbank "#1" auf Host "#2:#3" schlug fehl.',
2001 2022
  'The connection to the database could not be established.' => 'Die Verbindung zur Datenbank konnte nicht hergestellt werden.',
2002 2023
  'The connection to the template database failed:' => 'Die Verbindung zur Vorlagendatenbank schlug fehl:',
2003 2024
  'The connection was established successfully.' => 'Die Verbindung zur Datenbank wurde erfolgreich hergestellt.',
......
2042 2063
  'The following Datasets need to be updated' => 'Folgende Datenbanken müssen aktualisiert werden',
2043 2064
  'The following currencies have been used, but they are not defined:' => 'Die folgenden Währungen wurden benutzt, sind aber nicht ordnungsgemäß in der Datenbank eingetragen:',
2044 2065
  'The following drafts have been saved and can be loaded.' => 'Die folgenden Entwürfe wurden gespeichert und können geladen werden.',
2066
  'The following list has been generated automatically from existing users collapsing users with identical settings into a single entry.' => 'Die folgende Liste wurde automatisch aus den im System vorhandenen Benutzern zusammengestellt, wobei identische Einstellungen zu einem Eintrag zusammengefasst wurden.',
2045 2067
  'The following old files whose settings have to be merged manually into the new configuration file "config/kivitendo.conf" still exist:' => 'Es existieren noch die folgenden alten Dateien, deren Einstellungen manuell in die neue Konfiguratsdatei "config/kivitendo.conf" migriert werden müssen:',
2046 2068
  'The following transaction contains wrong taxes:' => 'Die folgende Buchung enthält falsche Steuern:',
2047 2069
  'The following transaction contains wrong taxkeys:' => 'Die folgende Buchung enthält falsche Steuerschlüssel:',
......
2123 2145
  'The unit in row %d has been deleted in the meantime.' => 'Die Einheit in Zeile %d ist in der Zwischentzeit gelöscht worden.',
2124 2146
  'The unit in row %d has been used in the meantime and cannot be changed anymore.' => 'Die Einheit in Zeile %d wurde in der Zwischenzeit benutzt und kann nicht mehr geändert werden.',
2125 2147
  'The units have been saved.'  => 'Die Einheiten wurden gespeichert.',
2148
  'The user can chose which client to connect to during login.' => 'Bei der Anmeldung kann der Benutzer auswählen, welchen Mandanten er benutzen möchte.',
2126 2149
  'The user is a member in the following group(s):' => 'Der Benutzer ist Mitglied in den folgenden Gruppen:',
2127 2150
  'The variable name must only consist of letters, numbers and underscores. It must begin with a letter. Example: send_christmas_present' => 'Der Variablenname darf nur aus Zeichen (keine Umlaute), Ziffern und Unterstrichen bestehen. Er muss mit einem Buchstaben beginnen. Beispiel: weihnachtsgruss_verschicken',
2128 2151
  'The warehouse could not be deleted because it has already been used.' => 'Das Lager konnte nicht gelöscht werden, da es bereits in Benutzung war.',
......
2160 2183
  'There is nothing to do in this step.' => 'In diesem Schritt gibt es nichts mehr zu tun.',
2161 2184
  'There was an error executing the background job.' => 'Bei der Ausführung des Hintergrund-Jobs trat ein Fehler auf.',
2162 2185
  'There was an error parsing the csv file: #1 in line #2.' => 'Es gab einen Fehler beim Parsen der CSV Datei: "#1" in der Zeile "#2"',
2186
  'Therefore several settings that had to be made for each user in the past have been consolidated into the client configuration.' => 'Dazu wurden gewisse Einstellungen, die vorher bei jedem Benutzer vorgenommen werden mussten, in die Konfiguration eines Mandanten verschoben.',
2163 2187
  'Therefore the definition of "kg" with the base unit "g" and a factor of 1000 is valid while defining "g" with a base unit of "kg" and a factor of "0.001" is not.' => 'So ist die Definition von "kg" mit der Basiseinheit "g" und dem Faktor 1000 zulässig, die Definition von "g" mit der Basiseinheit "kg" und dem Faktor "0,001" hingegen nicht.',
2164 2188
  'Therefore there\'s no need to create the same article more than once if it is sold or bought in/from another tax zone.' => 'Deswegen muss man den gleichen Artikel nicht mehr mehrmals anlegen, wenn er in verschiedenen Steuerzonen gehandelt werden soll.',
2165 2189
  'These units can be based on other units so that kivitendo can convert prices when the user switches from one unit to another.' => 'Einheiten können auf anderen Einheiten basieren, sodass kivitendo Preise automatisch umrechnen kann, wenn die Benutzer zwischen solchen Einheiten umschalten.',
......
2287 2311
  'Updated'                     => 'Erneuert am',
2288 2312
  'Updating existing entry in database' => 'Existierenden Eintrag in Datenbank aktualisieren',
2289 2313
  'Updating prices of existing entry in database' => 'Preis des Eintrags in der Datenbank wird aktualisiert',
2314
  'Updating the client fields in the database "#1" on host "#2:#3" failed.' => 'Die Aktualisierung der Mandantenfelder in der Datenbank "#1" auf Host "#2:#3" schlug fehl.',
2290 2315
  'Uploaded on #1, size #2 kB'  => 'Am #1 hochgeladen, Größe #2 kB',
2291 2316
  'Use As New'                  => 'Als neu verwenden',
2292 2317
  'Use Templates'               => 'Benutze Vorlagen',
......
2294 2319
  'User'                        => 'Benutzer',
2295 2320
  'User Config'                 => 'Einstellungen',
2296 2321
  'User Login'                  => 'Als Benutzer anmelden',
2322
  'User access'                 => 'Benutzerzugriff',
2323
  'User data migration'         => 'Benutzerdatenmigration',
2297 2324
  'User deleted!'               => 'Benutzer gelöscht!',
2298 2325
  'User login'                  => 'Benutzeranmeldung',
2299 2326
  'User name'                   => 'Benutzername',
2300 2327
  'User saved!'                 => 'Benutzer gespeichert!',
2301 2328
  'Username'                    => 'Benutzername',
2302 2329
  'Users in this group'         => 'BenutzerInnen in dieser Gruppe',
2330
  'Users with access'           => 'Benutzer mit Zugriff',
2331
  'Users with access to this client' => 'Benutzer mit Zugriff auf diesen Mandanten',
2303 2332
  'Ust-IDNr'                    => 'USt-IdNr.',
2333
  'VAT ID'                      => 'UStdID-Nr',
2304 2334
  'Valid'                       => 'Gültig',
2305 2335
  'Valid from'                  => 'Gültig ab',
2306 2336
  'Valid until'                 => 'gültig bis',
......
2403 2433
  'You have to enter a company name in your user preferences (see the "Program" menu, "Preferences").' => 'Sie müssen einen Firmennamen in Ihren Einstellungen angeben (siehe Menü "Programm", "Einstellungen").',
2404 2434
  'You have to enter the SEPA creditor ID in your user preferences (see the "Program" menu, "Preferences").' => 'Sie müssen die SEPA-Kreditoren-Identifikation in Ihren Einstellungen angeben (siehe Menü "Programm", "Einstellungen").',
2405 2435
  'You have to fill in at least an account number, the bank code, the IBAN and the BIC.' => 'Sie müssen zumindest die Kontonummer, die Bankleitzahl, die IBAN und den BIC angeben.',
2436
  'You have to grant users access to one or more clients.' => 'Benutzern muss dann Zugriff auf einzelne Mandanten gewährt werden.',
2406 2437
  'You have to specify a department.' => 'Sie müssen eine Abteilung wählen.',
2407 2438
  'You have to specify an execution date for each antry.' => 'Sie müssen für jeden zu buchenden Eintrag ein Ausführungsdatum angeben.',
2408 2439
  'You must chose a user.'      => 'Sie müssen einen Benutzer auswählen.',
......
2506 2537
  'kivitendo Homepage'          => 'Infos zu kivitendo',
2507 2538
  'kivitendo administration'    => 'kivitendo Administration',
2508 2539
  'kivitendo can fix these problems automatically.' => 'kivitendo kann solche Probleme automatisch beheben.',
2540
  'kivitendo has been extended to handle multiple clients within a single installation.' => 'kivitendo wurde um Mandantenfähigkeit erweitert.',
2541
  'kivitendo has been switched to group-based access restrictions.' => 'kivitendo wurde auf eine gruppenbasierte Benutzerzugriffsverwaltung umgestellt.',
2509 2542
  'kivitendo has found one or more problems in the general ledger.' => 'kivitendo hat ein oder mehrere Probleme im Hauptbuch gefunden.',
2510 2543
  'kivitendo is about to update the database [ #1 ].' => 'kivitendo wird gleich die Datenbank [ #1 ] aktualisieren.',
2511 2544
  'kivitendo is now able to manage warehouses instead of just tracking the amount of goods in your system.' => 'kivitendo enthält jetzt auch echte Lagerverwaultung anstatt reiner Mengenzählung.',
sql/Pg-upgrade2-auth/clients.pl
1
# @tag: clients
2
# @description: Einführung von Mandaten
3
# @depends: release_3_0_0
4
# @ignore: 0
5
package SL::DBUpgrade2::clients;
6

  
7
use strict;
8
use utf8;
9

  
10
use parent qw(SL::DBUpgrade2::Base);
11

  
12
use List::MoreUtils qw(any all);
13
use List::Util qw(first);
14

  
15
use SL::DBConnect;
16
use SL::DBUtils;
17
use SL::Template;
18
use SL::Helper::Flash;
19

  
20
use Rose::Object::MakeMethods::Generic (
21
  scalar                  => [ qw(clients) ],
22
  'scalar --get_set_init' => [ qw(users groups templates auth_db_settings data_dbhs) ],
23
);
24

  
25
sub init_users {
26
  my ($self) = @_;
27
  my @users  = selectall_hashref_query($::form, $self->dbh, qq|SELECT * FROM auth."user" ORDER BY lower(login)|);
28

  
29
  foreach my $user (@users) {
30
    my @attributes = selectall_hashref_query($::form, $self->dbh, <<SQL, $user->{id});
31
     SELECT cfg_key, cfg_value
32
     FROM auth.user_config
33
     WHERE user_id = ?
34
SQL
35

  
36
    $user->{ $_->{cfg_key} } = $_->{cfg_value} for @attributes;
37
  }
38

  
39
  return \@users;
40
}
41

  
42
sub init_groups {
43
  my ($self) = @_;
44
  return [ selectall_hashref_query($::form, $self->dbh, qq|SELECT * FROM auth."group" ORDER BY lower(name)|) ];
45
}
46

  
47
sub init_templates {
48
  my %templates = SL::Template->available_templates;
49
  return $templates{print_templates};
50
}
51

  
52
sub init_auth_db_settings {
53
  my $cfg = $::lx_office_conf{'authentication/database'};
54
  return {
55
    dbhost => $cfg->{host} || 'localhost',
56
    dbport => $cfg->{port} || 5432,
57
    dbname => $cfg->{name},
58
  };
59
}
60

  
61
sub init_data_dbhs {
62
  return [];
63
}
64

  
65
sub _clear_field {
66
  my ($text) = @_;
67

  
68
  $text ||= '';
69
  $text   =~ s/^\s+|\s+$//g;
70

  
71
  return $text;
72
}
73

  
74
sub _group_into_clients {
75
  my ($self) = @_;
76

  
77
  my @match_fields = qw(dbhost dbport dbname);
78
  my @copy_fields  = (@match_fields, qw(address company co_ustid dbuser dbpasswd duns sepa_creditor_id taxnumber templates));
79
  my @clients;
80

  
81
  # Group users into clients. Users which have identical database
82
  # settings (host, port and name) will be grouped. The other fields
83
  # like tax number etc. are taken from the first user and only filled
84
  # from user users if they're still unset.
85
  foreach my $user (@{ $self->users }) {
86
    $user->{$_} = _clear_field($user->{$_}) for @copy_fields;
87

  
88
    my $existing_client = first { my $client = $_; all { ($user->{$_} || '') eq ($client->{$_} || '') } @match_fields } @clients;
89

  
90
    if ($existing_client) {
91
      push @{ $existing_client->{users} }, $user->{id};
92
      $existing_client->{$_} ||= $user->{$_} for @copy_fields;
93
      next;
94
    }
95

  
96
    push @clients, {
97
      map({ $_ => $user->{$_} } @copy_fields),
98
      name    => $::locale->text('Client #1', scalar(@clients) + 1),
99
      users   => [ $user->{id} ],
100
      groups  => [ map { $_->{id} } @{ $self->groups } ],
101
      enabled => 1,
102
    };
103
  }
104

  
105
  # Ignore users (and therefore clients) for which no database
106
  # configuration has been given.
107
  @clients = grep { my $client = $_; any { $client->{$_} } @match_fields } @clients;
108

  
109
  # If there's only one client set that one as default.
110
  $clients[0]->{is_default} = 1 if scalar(@clients) == 1;
111

  
112
  # Set a couple of defaults for database fields.
113
  my $num = 0;
114
  foreach my $client (@clients) {
115
    $num                += 1;
116
    $client->{dbhost}  ||= 'localhost';
117
    $client->{dbport}  ||= 5432;
118
    $client->{templates} =~ s:templates/::;
119
  }
120

  
121
  $self->clients(\@clients);
122
}
123

  
124
sub _analyze {
125
  my ($self, %params) = @_;
126

  
127
  $self->_group_into_clients;
128

  
129
  return $self->_do_convert if !@{ $self->clients };
130

  
131
  print $::form->parse_html_template('dbupgrade/auth/clients', { SELF => $self });
132

  
133
  return 2;
134
}
135

  
136
sub _verify_clients {
137
  my ($self) = @_;
138

  
139
  my (%names, @errors);
140

  
141
  my $num = 0;
142
  foreach my $client (@{ $self->clients }) {
143
    $num += 1;
144

  
145
    next if !$client->{enabled};
146

  
147
    $client->{$_} = _clear_field($client->{$_}) for qw(address co_ustid company dbhost dbname dbpasswd dbport dbuser duns sepa_creditor_id taxnumber templates);
148

  
149
    if (!$client->{name} || $names{ $client->{name} }) {
150
      push @errors, $::locale->text('New client #1: The name must be unique and not empty.', $num);
151
    }
152

  
153
    $names{ $client->{name} } = 1;
154

  
155
    if (any { !$client->{$_} } qw(dbhost dbport dbname dbuser)) {
156
      push @errors, $::locale->text('New client #1: The database configuration fields "host", "port", "name" and "user" must not be empty.', $num);
157
    }
158
  }
159

  
160
  return @errors;
161
}
162

  
163
sub _alter_auth_database_structure {
164
  my ($self) = @_;
165

  
166
  my @queries = (
167
    qq|CREATE TABLE auth.clients (
168
         id         SERIAL  PRIMARY KEY,
169
         name       TEXT    NOT NULL UNIQUE,
170
         dbhost     TEXT    NOT NULL,
171
         dbport     INTEGER NOT NULL DEFAULT 5432,
172
         dbname     TEXT    NOT NULL,
173
         dbuser     TEXT    NOT NULL,
174
         dbpasswd   TEXT    NOT NULL,
175
         is_default BOOLEAN NOT NULL DEFAULT FALSE,
176

  
177
         UNIQUE (dbhost, dbport, dbname)
178
       )|,
179
    qq|CREATE TABLE auth.clients_users (
180
         client_id INTEGER NOT NULL REFERENCES auth.clients (id),
181
         user_id   INTEGER NOT NULL REFERENCES auth."user"  (id),
182

  
183
         PRIMARY KEY (client_id, user_id)
184
       )|,
185
    qq|CREATE TABLE auth.clients_groups (
186
         client_id INTEGER NOT NULL REFERENCES auth.clients (id),
187
         group_id  INTEGER NOT NULL REFERENCES auth."group" (id),
188

  
189
         PRIMARY KEY (client_id, group_id)
190
       )|,
191
  );
192

  
193
  $self->db_query($_, may_fail => 0) for @queries;
194
}
195

  
196
sub _alter_data_database_structure {
197
  my ($self, $dbh) = @_;
198

  
199
  my @queries = (
200
    qq|ALTER TABLE defaults ADD COLUMN company          TEXT|,
201
    qq|ALTER TABLE defaults ADD COLUMN address          TEXT|,
202
    qq|ALTER TABLE defaults ADD COLUMN taxnumber        TEXT|,
203
    qq|ALTER TABLE defaults ADD COLUMN co_ustid         TEXT|,
204
    qq|ALTER TABLE defaults ADD COLUMN duns             TEXT|,
205
    qq|ALTER TABLE defaults ADD COLUMN sepa_creditor_id TEXT|,
206
    qq|ALTER TABLE defaults ADD COLUMN templates        TEXT|,
207
    qq|INSERT INTO schema_info (tag, login) VALUES ('clients', 'admin')|,
208
  );
209

  
210
  foreach my $query (@queries) {
211
    $dbh->do($query) || die $self->db_errstr($dbh);
212
  }
213
}
214

  
215
sub _create_clients_in_auth_database {
216
  my ($self)  = @_;
217

  
218
  my @client_columns   = qw(name dbhost dbport dbname dbuser dbpasswd is_default);
219
  my $q_client         = qq|INSERT INTO auth.clients (| . join(', ', @client_columns) . qq|) VALUES (| . join(', ', ('?') x @client_columns) . qq|) RETURNING id|;
220
  my $sth_client       = $self->dbh->prepare($q_client) || die $self->db_errstr;
221

  
222
  my $q_client_user    = qq|INSERT INTO auth.clients_users (client_id, user_id) VALUES (?, ?)|;
223
  my $sth_client_user  = $self->dbh->prepare($q_client_user) || die $self->db_errstr;
224

  
225
  my $q_client_group   = qq|INSERT INTO auth.clients_groups (client_id, group_id) VALUES (?, ?)|;
226
  my $sth_client_group = $self->dbh->prepare($q_client_group) || die $self->db_errstr;
227

  
228
  foreach my $client (@{ $self->clients }) {
229
    next unless $client->{enabled};
230

  
231
    $client->{is_default} = $client->{is_default} ? 1 : 0;
232

  
233
    $sth_client->execute(@{ $client }{ @client_columns }) || die;
234
    my $client_id = $sth_client->fetch->[0];
235

  
236
    $sth_client_user ->execute($client_id, $_) || die for @{ $client->{users}  || [] };
237
    $sth_client_group->execute($client_id, $_) || die for @{ $client->{groups} || [] };
238
  }
239

  
240
  $sth_client      ->finish;
241
  $sth_client_user ->finish;
242
  $sth_client_group->finish;
243
}
244

  
245
sub _clean_auth_database {
246
  my ($self) = @_;
247

  
248
  my @keys_to_delete = qw(acs address admin anfragen angebote bestellungen businessnumber charset companies company co_ustid currency dbconnect dbdriver dbhost dbname dboptions dbpasswd dbport dbuser duns
249
                          einkaufsrechnungen in_numberformat lieferantenbestellungen login pdonumber printer rechnungen role sdonumber sepa_creditor_id sid steuernummer taxnumber templates);
250

  
251
  $self->dbh->do(qq|DELETE FROM auth.user_config WHERE cfg_key IN (| . join(', ', ('?') x @keys_to_delete) . qq|)|, undef, @keys_to_delete)
252
    || die $self->db_errstr;
253
}
254

  
255
sub _copy_fields_to_data_database {
256
  my ($self, $client) = @_;
257

  
258
  my $dbh = SL::DBConnect->connect('dbi:Pg:dbname=' . $client->{dbname} . ';host=' . $client->{dbhost} . ';port=' . $client->{dbport},
259
                                   $client->{dbuser}, $client->{dbpasswd},
260
                                   SL::DBConnect->get_options(AutoCommit => 0));
261
  if (!$dbh) {
262
    die join("\n",
263
             $::locale->text('The connection to the configured client database "#1" on host "#2:#3" failed.', $client->{dbname}, $client->{dbhost}, $client->{dbport}),
264
             $::locale->text('Please correct the settings and try again or deactivate that client.'),
265
             $::locale->text('Error message from the database: #1', $self->db_errstr('DBI')));
266
  }
267

  
268
  my ($has_been_applied) = $dbh->selectrow_array(qq|SELECT tag FROM schema_info WHERE tag = 'clients'|);
269

  
270
  if (!$has_been_applied) {
271
    $self->_alter_data_database_structure($dbh);
272
  }
273

  
274
  my @columns = qw(company address taxnumber co_ustid duns sepa_creditor_id templates);
275
  my $query   = join ', ', map { "$_ = ?" } @columns;
276
  my @values  = @{ $client }{ @columns };
277

  
278
  if (!$dbh->do(qq|UPDATE defaults SET $query|, undef, @values)) {
279
    die join("\n",
280
             $::locale->text('Updating the client fields in the database "#1" on host "#2:#3" failed.', $client->{dbname}, $client->{dbhost}, $client->{dbport}),
281
             $::locale->text('Please correct the settings and try again or deactivate that client.'),
282
             $::locale->text('Error message from the database: #1', $self->db_errstr('DBI')));
283
  }
284

  
285
  $self->data_dbhs([ @{ $self->data_dbhs }, $dbh ]);
286
}
287

  
288
sub _commit_data_database_changes {
289
  my ($self) = @_;
290

  
291
  foreach my $dbh (@{ $self->data_dbhs }) {
292
    $dbh->commit;
293
    $dbh->disconnect;
294
  }
295
}
296

  
297
sub _do_convert {
298
  my ($self) = @_;
299

  
300
  # Skip clients that are not enabled. Clean fields.
301
  my $num = 0;
302
  foreach my $client (@{ $self->clients }) {
303
    $num += 1;
304

  
305
    next if !$client->{enabled};
306

  
307
    $client->{$_}        = _clear_field($client->{$_}) for qw(dbhost dbport dbname dbuser dbpasswd address company co_ustid dbuser dbpasswd duns sepa_creditor_id taxnumber templates);
308
    $client->{templates} = 'templates/' . $client->{templates};
309
  }
310

  
311
  $self->_copy_fields_to_data_database($_) for grep { $_->{enabled} } @{ $self->clients };
312

  
313
  $self->_alter_auth_database_structure;
314
  $self->_create_clients_in_auth_database;
315
  $self->_clean_auth_database;
316

  
317
  $self->_commit_data_database_changes;
318

  
319
  return 1;
320
}
321

  
322
sub run {
323
  my ($self) = @_;
324

  
325
  return $self->_analyze if !$::form->{clients} || !@{ $::form->{clients} };
326

  
327
  $self->clients($::form->{clients});
328

  
329
  my @errors = $self->_verify_clients;
330

  
331
  return $self->_do_convert if !@errors;
332

  
333
  flash('error', @errors);
334

  
335
  print $::form->parse_html_template('dbupgrade/auth/clients', { SELF => $self });
336

  
337
  return 1;
338
}
339

  
340
1;
templates/webpages/dbupgrade/auth/clients.html
1
[%- USE LxERP -%][%- USE L -%]
2

  
3
[%- INCLUDE 'common/flash.html' %]
4

  
5
[% L.javascript_tag('jquery.selectboxes', 'jquery.multiselect2side') %]
6

  
7
<h1>[%- LxERP.t8("Introduction of clients") %]</h1>
8

  
9
<p>
10
 [% LxERP.t8("kivitendo has been extended to handle multiple clients within a single installation.") %]
11
 [% LxERP.t8("Therefore several settings that had to be made for each user in the past have been consolidated into the client configuration.") %]
12
 [% LxERP.t8("You have to grant users access to one or more clients.") %]
13
 [% LxERP.t8("The user can chose which client to connect to during login.") %]
14
</p>
15

  
16
<p>
17
 [% LxERP.t8("The access rights a user has within a client instance is still governed by his group membership.") %]
18
 [% LxERP.t8("Only groups that have been configured for the client the user logs in to will be considered.") %]
19
</p>
20

  
21
<p>
22
 [% LxERP.t8("The following list has been generated automatically from existing users collapsing users with identical settings into a single entry.") %]
23
 [% LxERP.t8("Please select which client configurations you want to create.") %]
24
 [% LxERP.t8("The 'name' is the field shown to the user during login.") %]
25
 [% LxERP.t8("It can be changed later but must be unique within the installation.") %]
26
</p>
27

  
28
<form method="post" action="admin.pl">
29
 [%- FOREACH client = SELF.clients %]
30
 [%- L.hidden_tag("clients[+].dummy", 1) %]
31

  
32
 <h2>[%- L.checkbox_tag("clients[].enabled", label=LxERP.t8("Create new client #1", loop.count), checked=client.enabled) %]</h2>
33

  
34
 <table>
35
  <tr>
36
   <th colspan="6">[%- LxERP.t8("General settings") %]</th>
37
  </tr>
38

  
39
  <tr>
40
   <td align="right" valign="top">[%- LxERP.t8("Client name") %]:</td>
41
   <td valign="top">[%- L.input_tag("clients[].name", client.name) %]</td>
42

  
43
   <td align="right" valign="top">[%- LxERP.t8("Company name") %]:</td>
44
   <td valign="top">[%- L.input_tag("clients[].company", client.company) %]</td>
45

  
46
   <td align="right" valign="top">[%- LxERP.t8("Address") %]:</td>
47
   <td valign="top">[%- L.textarea_tag("clients[].address", client.address, rows=4, cols=40) %]</td>
48
  </tr>
49

  
50
  <tr>
51
   <td align="right">[%- LxERP.t8("Tax number") %]:</td>
52
   <td>[%- L.input_tag("clients[].taxnumber", client.taxnumber) %]</td>
53

  
54
   <td align="right">[%- LxERP.t8("VAT ID") %]:</td>
55
   <td>[%- L.input_tag("clients[].co_ustid", client.co_ustid) %]</td>
56

  
57
   <td align="right">[%- LxERP.t8("DUNS-Nr") %]:</td>
58
   <td>[%- L.input_tag("clients[].duns", client.duns) %]</td>
59
  </tr>
60

  
61
  <tr>
62
   <td align="right">[%- LxERP.t8("SEPA creditor ID") %]:</td>
63
   <td colspan="5">[%- L.input_tag("clients[].sepa_creditor_id", client.sepa_creditor_id) %]</td>
64
  </tr>
65

  
66
  <tr>
67
   <td align="right">[%- LxERP.t8("Print templates") %]:</td>
68
   <td colspan="5">[%- L.select_tag("clients[].templates", SELF.templates, default=client.templates) %]</td>
69
  </tr>
70

  
71
  <tr>
72
   <th colspan="6">[%- LxERP.t8("User access") %]</th>
73
  </tr>
74

  
75
  <tr>
76
   <td valign="top">[%- LxERP.t8("Users with access to this client") %]:</td>
77

  
78
   <td valign="top" colspan="6" class="clearfix">
79
    [% L.select_tag('clients[].users[]', SELF.users, id='users_multi_' _ loop.count, value_key='id', title_key='login', default=client.users, multiple=1) %]
80
   </td>
81
  </tr>
82

  
83
  <tr>
84
   <td valign="top">[%- LxERP.t8("Groups that are valid for this client for access rights") %]:</td>
85

  
86
   <td valign="top" colspan="6" class="clearfix">
87
    [% L.select_tag('clients[].groups[]', SELF.groups, id='groups_multi_' _ loop.count, value_key='id', title_key='name', default=client.groups, multiple=1) %]
88
   </td>
89
  </tr>
90

  
91
  <tr>
92
   <th colspan="6">[%- LxERP.t8("Database settings") %]</th>
93
  </tr>
94

  
95
  <tr>
96
   <td align="right">[%- LxERP.t8("Database Host") %]:</td>
97
   <td>[%- L.input_tag("clients[].dbhost", client.dbhost) %]</td>
98

  
99
   <td align="right">[%- LxERP.t8("Port") %]:</td>
100
   <td>[%- L.input_tag("clients[].dbport", (client.dbport || 5432)) %]</td>
101

  
102
   <td align="right">[%- LxERP.t8("Database name") %]:</td>
103
   <td>[%- L.input_tag("clients[].dbname", client.dbname) %]</td>
104
  </tr>
105

  
106
  <tr>
107
   <td align="right">[%- LxERP.t8("User") %]:</td>
108
   <td>[%- L.input_tag("clients[].dbuser", client.dbuser) %]</td>
109

  
110
   <td align="right">[%- LxERP.t8("Password") %]:</td>
111
   <td>[%- L.input_tag("clients[].dbpasswd", client.dbpasswd) %]</td>
112
  </tr>
113

  
114
 </table>
115

  
116
 [% L.multiselect2side('users_multi_'  _ loop.count, labelsx => LxERP.t8('All users'),  labeldx => LxERP.t8('Users with access')) %]
117
 [% L.multiselect2side('groups_multi_' _ loop.count, labelsx => LxERP.t8('All groups'), labeldx => LxERP.t8('Groups valid for this client')) %]
118
 [%- END %]
119

  
120
 <p>
121
  [%- L.hidden_tag('action', 'list_users') %]
122
  [% L.submit_tag('dummy', LxERP.t8('Continue')) %]
123
 </p>
124
</form>

Auch abrufbar als: Unified diff