2 # @description: Einführung von Mandanten
3 # @depends: release_3_0_0
5 package SL::DBUpgrade2::clients;
10 use parent qw(SL::DBUpgrade2::Base);
12 use List::MoreUtils qw(any all);
13 use List::Util qw(first);
18 use SL::Helper::Flash;
20 use Rose::Object::MakeMethods::Generic (
21 scalar => [ qw(clients) ],
22 'scalar --get_set_init' => [ qw(users groups templates auth_db_settings data_dbhs) ],
27 my @users = selectall_hashref_query($::form, $self->dbh, qq|SELECT * FROM auth."user" ORDER BY lower(login)|);
29 foreach my $user (@users) {
30 my @attributes = selectall_hashref_query($::form, $self->dbh, <<SQL, $user->{id});
31 SELECT cfg_key, cfg_value
36 $user->{ $_->{cfg_key} } = $_->{cfg_value} for @attributes;
44 return [ selectall_hashref_query($::form, $self->dbh, qq|SELECT * FROM auth."group" ORDER BY lower(name)|) ];
48 my %templates = SL::Template->available_templates;
49 return $templates{print_templates};
52 sub init_auth_db_settings {
53 my $cfg = $::lx_office_conf{'authentication/database'};
55 dbhost => $cfg->{host} || 'localhost',
56 dbport => $cfg->{port} || 5432,
57 dbname => $cfg->{name},
69 $text =~ s/^\s+|\s+$//g;
74 sub _group_into_clients {
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));
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;
88 my $existing_client = first { my $client = $_; all { ($user->{$_} || '') eq ($client->{$_} || '') } @match_fields } @clients;
90 if ($existing_client) {
91 push @{ $existing_client->{users} }, $user->{id};
92 $existing_client->{$_} ||= $user->{$_} for @copy_fields;
97 map({ $_ => $user->{$_} } @copy_fields),
98 users => [ $user->{id} ],
99 groups => [ map { $_->{id} } @{ $self->groups } ],
104 # Ignore users (and therefore clients) for which no database
105 # configuration has been given.
106 @clients = grep { my $client = $_; any { $client->{$_} } @match_fields } @clients;
108 # If there's only one client set that one as default.
109 $clients[0]->{is_default} = 1 if scalar(@clients) == 1;
111 # Set a couple of defaults for database fields.
113 foreach my $client (@clients) {
115 $client->{name} ||= $::locale->text('Client #1', $num);
116 $client->{dbhost} ||= 'localhost';
117 $client->{dbport} ||= 5432;
118 $client->{templates} =~ s:templates/::;
121 $self->clients(\@clients);
125 my ($self, %params) = @_;
127 $self->_group_into_clients;
129 return $self->_do_convert if !@{ $self->clients };
131 print $::form->parse_html_template('dbupgrade/auth/clients', { SELF => $self });
136 sub _verify_clients {
139 my (%names, @errors);
142 foreach my $client (@{ $self->clients }) {
145 next if !$client->{enabled};
147 $client->{$_} = _clear_field($client->{$_}) for qw(address co_ustid company dbhost dbname dbpasswd dbport dbuser duns sepa_creditor_id taxnumber templates);
149 if (!$client->{name} || $names{ $client->{name} }) {
150 push @errors, $::locale->text('New client #1: The name must be unique and not empty.', $num);
153 $names{ $client->{name} } = 1;
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);
163 sub _alter_auth_database_structure {
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,
177 UNIQUE (dbhost, dbport, dbname)
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),
183 PRIMARY KEY (client_id, user_id)
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),
189 PRIMARY KEY (client_id, group_id)
193 $self->db_query($_, may_fail => 0) for @queries;
196 sub _alter_data_database_structure {
197 my ($self, $dbh) = @_;
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')|,
210 foreach my $query (@queries) {
211 $dbh->do($query) || die $self->db_errstr($dbh);
215 sub _create_clients_in_auth_database {
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;
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;
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;
228 foreach my $client (@{ $self->clients }) {
229 next unless $client->{enabled};
231 $client->{is_default} = $client->{is_default} ? 1 : 0;
233 $sth_client->execute(@{ $client }{ @client_columns }) || die;
234 my $client_id = $sth_client->fetch->[0];
236 $sth_client_user ->execute($client_id, $_) || die for @{ $client->{users} || [] };
237 $sth_client_group->execute($client_id, $_) || die for @{ $client->{groups} || [] };
240 $sth_client ->finish;
241 $sth_client_user ->finish;
242 $sth_client_group->finish;
245 sub _clean_auth_database {
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);
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;
255 sub _copy_fields_to_data_database {
256 my ($self, $client) = @_;
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));
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')));
268 my ($has_been_applied) = $dbh->selectrow_array(qq|SELECT tag FROM schema_info WHERE tag = 'clients'|);
270 if (!$has_been_applied) {
271 $self->_alter_data_database_structure($dbh);
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 };
278 if (!$dbh->do(qq|UPDATE defaults SET $query|, undef, @values)) {
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')));
285 $self->data_dbhs([ @{ $self->data_dbhs }, $dbh ]);
288 sub _commit_data_database_changes {
291 foreach my $dbh (@{ $self->data_dbhs }) {
300 # Skip clients that are not enabled. Clean fields.
302 foreach my $client (@{ $self->clients }) {
305 next if !$client->{enabled};
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};
311 $self->_copy_fields_to_data_database($_) for grep { $_->{enabled} } @{ $self->clients };
313 $self->_alter_auth_database_structure;
314 $self->_create_clients_in_auth_database;
315 $self->_clean_auth_database;
317 $self->_commit_data_database_changes;
325 return $self->_analyze if !$::form->{clients} || !@{ $::form->{clients} };
327 $self->clients($::form->{clients});
329 my @errors = $self->_verify_clients;
331 return $self->_do_convert if !@errors;
333 flash('error', @errors);
335 print $::form->parse_html_template('dbupgrade/auth/clients', { SELF => $self });