1# --
2# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/
3# --
4# This software comes with ABSOLUTELY NO WARRANTY. For details, see
5# the enclosed file COPYING for license information (GPL). If you
6# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
7# --
8
9package Kernel::System::CustomerCompany::DB;
10
11use strict;
12use warnings;
13
14use Kernel::System::VariableCheck qw(:all);
15
16our @ObjectDependencies = (
17    'Kernel::System::Cache',
18    'Kernel::System::DB',
19    'Kernel::System::DynamicField',
20    'Kernel::System::DynamicField::Backend',
21    'Kernel::System::Log',
22    'Kernel::System::Valid',
23);
24
25sub new {
26    my ( $Type, %Param ) = @_;
27
28    # allocate new hash for object
29    my $Self = {};
30    bless( $Self, $Type );
31
32    # get customer company map
33    $Self->{CustomerCompanyMap} = $Param{CustomerCompanyMap} || die "Got no CustomerCompanyMap!";
34
35    # config options
36    $Self->{CustomerCompanyTable} = $Self->{CustomerCompanyMap}->{Params}->{Table}
37        || die "Need CustomerCompany->Params->Table in Kernel/Config.pm!";
38    $Self->{CustomerCompanyKey} = $Self->{CustomerCompanyMap}->{CustomerCompanyKey}
39        || die "Need CustomerCompany->CustomerCompanyKey in Kernel/Config.pm!";
40    $Self->{CustomerCompanyValid} = $Self->{CustomerCompanyMap}->{'CustomerCompanyValid'};
41    $Self->{SearchListLimit}      = $Self->{CustomerCompanyMap}->{'CustomerCompanySearchListLimit'} || 50000;
42    $Self->{SearchPrefix}         = $Self->{CustomerCompanyMap}->{'CustomerCompanySearchPrefix'};
43
44    if ( !defined( $Self->{SearchPrefix} ) ) {
45        $Self->{SearchPrefix} = '';
46    }
47    $Self->{SearchSuffix} = $Self->{CustomerCompanyMap}->{'CustomerCompanySearchSuffix'};
48    if ( !defined( $Self->{SearchSuffix} ) ) {
49        $Self->{SearchSuffix} = '*';
50    }
51
52    # create cache object, but only if CacheTTL is set in customer config
53    if ( $Self->{CustomerCompanyMap}->{CacheTTL} ) {
54        $Self->{CacheObject} = $Kernel::OM->Get('Kernel::System::Cache');
55        $Self->{CacheType}   = 'CustomerCompany' . $Param{Count};
56        $Self->{CacheTTL}    = $Self->{CustomerCompanyMap}->{CacheTTL} || 0;
57    }
58
59    # get database object
60    $Self->{DBObject} = $Kernel::OM->Get('Kernel::System::DB');
61
62    # create new db connect if DSN is given
63    if ( $Self->{CustomerCompanyMap}->{Params}->{DSN} ) {
64        $Self->{DBObject} = Kernel::System::DB->new(
65            DatabaseDSN  => $Self->{CustomerCompanyMap}->{Params}->{DSN},
66            DatabaseUser => $Self->{CustomerCompanyMap}->{Params}->{User},
67            DatabasePw   => $Self->{CustomerCompanyMap}->{Params}->{Password},
68            Type         => $Self->{CustomerCompanyMap}->{Params}->{Type} || '',
69        ) || die('Can\'t connect to database!');
70
71        # remember that we have the DBObject not from parent call
72        $Self->{NotParentDBObject} = 1;
73    }
74
75    # this setting specifies if the table has the create_time,
76    # create_by, change_time and change_by fields of OTRS
77    $Self->{ForeignDB} = $Self->{CustomerCompanyMap}->{Params}->{ForeignDB} ? 1 : 0;
78
79    # defines if the database search will be performend case sensitive (1) or not (0)
80    $Self->{CaseSensitive} = $Self->{CustomerCompanyMap}->{Params}->{SearchCaseSensitive}
81        // $Self->{CustomerCompanyMap}->{Params}->{CaseSensitive} || 0;
82
83    # fetch names of configured dynamic fields
84    my @DynamicFieldMapEntries = grep { $_->[5] eq 'dynamic_field' } @{ $Self->{CustomerCompanyMap}->{Map} };
85    $Self->{ConfiguredDynamicFieldNames} = { map { $_->[2] => 1 } @DynamicFieldMapEntries };
86
87    return $Self;
88}
89
90sub CustomerCompanyList {
91    my ( $Self, %Param ) = @_;
92
93    # check needed stuff
94    my $Valid = 1;
95    if ( !$Param{Valid} && defined( $Param{Valid} ) ) {
96        $Valid = 0;
97    }
98
99    my $Limit = $Param{Limit} // $Self->{SearchListLimit};
100
101    my $CacheType;
102    my $CacheKey;
103
104    # check cache
105    if ( $Self->{CacheObject} ) {
106
107        $CacheType = $Self->{CacheType} . '_CustomerCompanyList';
108        $CacheKey  = "CustomerCompanyList::${Valid}::${Limit}::" . ( $Param{Search} || '' );
109
110        my $Data = $Self->{CacheObject}->Get(
111            Type => $CacheType,
112            Key  => $CacheKey,
113        );
114        return %{$Data} if ref $Data eq 'HASH';
115    }
116
117    my $CustomerCompanyListFields = $Self->{CustomerCompanyMap}->{CustomerCompanyListFields};
118    if ( !IsArrayRefWithData($CustomerCompanyListFields) ) {
119        $CustomerCompanyListFields = [ 'customer_id', 'name', ];
120    }
121
122    # remove dynamic field names that are configured in CustomerCompanyListFields
123    # as they cannot be handled here
124    my @CustomerCompanyListFieldsWithoutDynamicFields
125        = grep { !exists $Self->{ConfiguredDynamicFieldNames}->{$_} } @{$CustomerCompanyListFields};
126
127    # what is the result
128    my $What = join(
129        ', ',
130        @CustomerCompanyListFieldsWithoutDynamicFields
131    );
132
133    # add valid option if required
134    my $SQL;
135    my @Bind;
136    my @Conditions;
137
138    if ( $Valid && $Self->{CustomerCompanyValid} ) {
139
140        # get valid object
141        my $ValidObject = $Kernel::OM->Get('Kernel::System::Valid');
142
143        push @Conditions, "$Self->{CustomerCompanyValid} IN ( ${\(join ', ', $ValidObject->ValidIDsGet())} )";
144    }
145
146    # where
147    if ( $Param{Search} ) {
148
149        # remove dynamic field names that are configured in CustomerCompanySearchFields
150        # as they cannot be retrieved here
151        my @CustomerCompanySearchFields = grep { !exists $Self->{ConfiguredDynamicFieldNames}->{$_} }
152            @{ $Self->{CustomerCompanyMap}->{CustomerCompanySearchFields} };
153
154        my %QueryCondition = $Self->{DBObject}->QueryCondition(
155            Key           => \@CustomerCompanySearchFields,
156            Value         => $Param{Search},
157            SearchPrefix  => $Self->{SearchPrefix},
158            SearchSuffix  => $Self->{SearchSuffix},
159            CaseSensitive => $Self->{CaseSensitive},
160            BindMode      => 1,
161        );
162
163        if ( $QueryCondition{SQL} ) {
164            push @Conditions, " $QueryCondition{SQL}";
165            push @Bind,       @{ $QueryCondition{Values} };
166        }
167    }
168
169    # dynamic field handling
170    my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
171
172    my $DynamicFieldConfigs = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
173        ObjectType => 'CustomerCompany',
174        Valid      => 1,
175    );
176    my %DynamicFieldConfigsByName = map { $_->{Name} => $_ } @{$DynamicFieldConfigs};
177
178    my @CustomerCompanyListFieldsDynamicFields
179        = grep { exists $Self->{ConfiguredDynamicFieldNames}->{$_} } @{$CustomerCompanyListFields};
180
181    # sql
182    my $CompleteSQL = "SELECT $Self->{CustomerCompanyKey}, $What FROM $Self->{CustomerCompanyTable}";
183
184    if (@Conditions) {
185        $SQL = join( ' AND ', @Conditions );
186        $CompleteSQL .= " WHERE $SQL";
187    }
188
189    # get data from customer company table
190    $Self->{DBObject}->Prepare(
191        SQL   => $CompleteSQL,
192        Bind  => \@Bind,
193        Limit => $Limit,
194    );
195
196    my @CustomerCompanyData;
197    while ( my @Row = $Self->{DBObject}->FetchrowArray() ) {
198        push @CustomerCompanyData, [@Row];
199    }
200
201    my %List;
202
203    CUSTOMERCOMPANYDATA:
204    for my $CustomerCompanyData (@CustomerCompanyData) {
205        my $CustomerCompanyID = shift @{$CustomerCompanyData};
206        next CUSTOMERCOMPANYDATA if $List{$CustomerCompanyID};
207
208        my %CompanyStringParts;
209
210        my $FieldCounter = 0;
211        for my $Field ( @{$CustomerCompanyData} ) {
212            $CompanyStringParts{ $CustomerCompanyListFieldsWithoutDynamicFields[$FieldCounter] } = $Field;
213            $FieldCounter++;
214        }
215
216        # fetch dynamic field values, if configured
217        if (@CustomerCompanyListFieldsDynamicFields) {
218            DYNAMICFIELDNAME:
219            for my $DynamicFieldName (@CustomerCompanyListFieldsDynamicFields) {
220                next DYNAMICFIELDNAME if !exists $DynamicFieldConfigsByName{$DynamicFieldName};
221
222                my $Value = $DynamicFieldBackendObject->ValueGet(
223                    DynamicFieldConfig => $DynamicFieldConfigsByName{$DynamicFieldName},
224                    ObjectName         => $CustomerCompanyID,
225                );
226
227                next DYNAMICFIELDNAME if !defined $Value;
228
229                if ( !IsArrayRefWithData($Value) ) {
230                    $Value = [$Value];
231                }
232
233                my @Values;
234
235                VALUE:
236                for my $CurrentValue ( @{$Value} ) {
237                    next VALUE if !defined $CurrentValue || !length $CurrentValue;
238
239                    my $ReadableValue = $DynamicFieldBackendObject->ReadableValueRender(
240                        DynamicFieldConfig => $DynamicFieldConfigsByName{$DynamicFieldName},
241                        Value              => $CurrentValue,
242                    );
243
244                    next VALUE if !IsHashRefWithData($ReadableValue) || !defined $ReadableValue->{Value};
245
246                    my $IsACLReducible = $DynamicFieldBackendObject->HasBehavior(
247                        DynamicFieldConfig => $DynamicFieldConfigsByName{$DynamicFieldName},
248                        Behavior           => 'IsACLReducible',
249                    );
250                    if ($IsACLReducible) {
251                        my $PossibleValues = $DynamicFieldBackendObject->PossibleValuesGet(
252                            DynamicFieldConfig => $DynamicFieldConfigsByName{$DynamicFieldName},
253                        );
254
255                        if (
256                            IsHashRefWithData($PossibleValues)
257                            && defined $PossibleValues->{ $ReadableValue->{Value} }
258                            )
259                        {
260                            $ReadableValue->{Value} = $PossibleValues->{ $ReadableValue->{Value} };
261                        }
262                    }
263
264                    push @Values, $ReadableValue->{Value};
265                }
266
267                $CompanyStringParts{$DynamicFieldName} = join ' ', @Values;
268            }
269        }
270
271        # assemble company string
272        my @CompanyStringParts;
273        CUSTOMERCOMPANYLISTFIELD:
274        for my $CustomerCompanyListField ( @{$CustomerCompanyListFields} ) {
275            next CUSTOMERCOMPANYLISTFIELD
276                if !exists $CompanyStringParts{$CustomerCompanyListField}
277                || !defined $CompanyStringParts{$CustomerCompanyListField}
278                || !length $CompanyStringParts{$CustomerCompanyListField};
279            push @CompanyStringParts, $CompanyStringParts{$CustomerCompanyListField};
280        }
281
282        $List{$CustomerCompanyID} = join ' ', @CompanyStringParts;
283    }
284
285    # cache request
286    if ( $Self->{CacheObject} ) {
287        $Self->{CacheObject}->Set(
288            Type  => $CacheType,
289            Key   => $CacheKey,
290            Value => \%List,
291            TTL   => $Self->{CacheTTL},
292        );
293    }
294
295    return %List;
296}
297
298sub CustomerCompanySearchDetail {
299    my ( $Self, %Param ) = @_;
300
301    if ( ref $Param{SearchFields} ne 'ARRAY' ) {
302        $Kernel::OM->Get('Kernel::System::Log')->Log(
303            Priority => 'error',
304            Message  => "SearchFields must be an array reference!",
305        );
306        return;
307    }
308
309    my $Valid = defined $Param{Valid} ? $Param{Valid} : 1;
310
311    $Param{Limit} //= '';
312
313    # Split the search fields in scalar and array fields.
314    my @ScalarSearchFields = grep { 'Input' eq $_->{Type} } @{ $Param{SearchFields} };
315    my @ArraySearchFields  = grep { 'Selection' eq $_->{Type} } @{ $Param{SearchFields} };
316
317    # Verify that all passed array parameters contain an arrayref.
318    ARGUMENT:
319    for my $Argument (@ArraySearchFields) {
320        if ( !defined $Param{ $Argument->{Name} } ) {
321            $Param{ $Argument->{Name} } ||= [];
322
323            next ARGUMENT;
324        }
325
326        if ( ref $Param{ $Argument->{Name} } ne 'ARRAY' ) {
327            $Kernel::OM->Get('Kernel::System::Log')->Log(
328                Priority => 'error',
329                Message  => "$Argument->{Name} must be an array reference!",
330            );
331            return;
332        }
333    }
334
335    # Set the default behaviour for the return type.
336    my $Result = $Param{Result} || 'ARRAY';
337
338    # Special handling if the result type is 'COUNT'.
339    if ( $Result eq 'COUNT' ) {
340
341        # Ignore the parameter 'Limit' when result type is 'COUNT'.
342        $Param{Limit} = '';
343
344        # Delete the OrderBy parameter when the result type is 'COUNT'.
345        $Param{OrderBy} = [];
346    }
347
348    # Define order table from the search fields.
349    my %OrderByTable = map { $_->{Name} => $_->{DatabaseField} } @{ $Param{SearchFields} };
350
351    for my $Field (@ArraySearchFields) {
352
353        my $SelectionsData = $Field->{SelectionsData};
354
355        for my $SelectedValue ( @{ $Param{ $Field->{Name} } } ) {
356
357            # Check if the selected value for the current field is valid.
358            if ( !$SelectionsData->{$SelectedValue} ) {
359                $Kernel::OM->Get('Kernel::System::Log')->Log(
360                    Priority => 'error',
361                    Message  => "The selected value $Field->{Name} is not valid!",
362                );
363                return;
364            }
365        }
366    }
367
368    my $DBObject = $Kernel::OM->Get('Kernel::System::DB');
369
370    # Assemble the conditions used in the WHERE clause.
371    my @SQLWhere;
372
373    for my $Field (@ScalarSearchFields) {
374
375        # Search for scalar fields (wildcards are allowed).
376        if ( $Param{ $Field->{Name} } ) {
377
378            # Get like escape string needed for some databases (e.g. oracle).
379            my $LikeEscapeString = $DBObject->GetDatabaseFunction('LikeEscapeString');
380
381            $Param{ $Field->{Name} } = $DBObject->Quote( $Param{ $Field->{Name} }, 'Like' );
382
383            $Param{ $Field->{Name} } =~ s{ \*+ }{%}xmsg;
384
385            # If the field contains more than only '%'.
386            if ( $Param{ $Field->{Name} } !~ m{ \A %* \z }xms ) {
387                push @SQLWhere,
388                    "LOWER($Field->{DatabaseField}) LIKE LOWER('$Param{ $Field->{Name} }') $LikeEscapeString";
389            }
390        }
391    }
392
393    my $DynamicFieldObject        = $Kernel::OM->Get('Kernel::System::DynamicField');
394    my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
395
396    # Check all configured change dynamic fields, build lookup hash by name.
397    my %CustomerCompanyDynamicFieldName2Config;
398    my $CustomerCompanyDynamicFields = $DynamicFieldObject->DynamicFieldListGet(
399        ObjectType => 'CustomerCompany',
400    );
401    for my $DynamicField ( @{$CustomerCompanyDynamicFields} ) {
402        $CustomerCompanyDynamicFieldName2Config{ $DynamicField->{Name} } = $DynamicField;
403    }
404
405    my $SQLDynamicFieldFrom     = '';
406    my $SQLDynamicFieldWhere    = '';
407    my $DynamicFieldJoinCounter = 1;
408
409    DYNAMICFIELD:
410    for my $DynamicField ( @{$CustomerCompanyDynamicFields} ) {
411
412        my $SearchParam = $Param{ "DynamicField_" . $DynamicField->{Name} };
413
414        next DYNAMICFIELD if ( !$SearchParam );
415        next DYNAMICFIELD if ( ref $SearchParam ne 'HASH' );
416
417        my $NeedJoin;
418
419        for my $Operator ( sort keys %{$SearchParam} ) {
420
421            my @SearchParams = ( ref $SearchParam->{$Operator} eq 'ARRAY' )
422                ? @{ $SearchParam->{$Operator} }
423                : ( $SearchParam->{$Operator} );
424
425            my $SQLDynamicFieldWhereSub = '';
426            if ($SQLDynamicFieldWhere) {
427                $SQLDynamicFieldWhereSub = ' AND (';
428            }
429            else {
430                $SQLDynamicFieldWhereSub = ' (';
431            }
432
433            my $Counter = 0;
434            TEXT:
435            for my $Text (@SearchParams) {
436                next TEXT if ( !defined $Text || $Text eq '' );
437
438                $Text =~ s/\*/%/gi;
439
440                # Check search attribute, we do not need to search for '*'.
441                next TEXT if $Text =~ /^\%{1,3}$/;
442
443                my $ValidateSuccess = $DynamicFieldBackendObject->ValueValidate(
444                    DynamicFieldConfig => $DynamicField,
445                    Value              => $Text,
446                    UserID             => $Param{UserID} || 1,
447                );
448                if ( !$ValidateSuccess ) {
449                    $Kernel::OM->Get('Kernel::System::Log')->Log(
450                        Priority => 'error',
451                        Message  => "Search not executed due to invalid value '"
452                            . $Text
453                            . "' on field '"
454                            . $DynamicField->{Name} . "'!",
455                    );
456                    return;
457                }
458
459                if ($Counter) {
460                    $SQLDynamicFieldWhereSub .= ' OR ';
461                }
462                $SQLDynamicFieldWhereSub .= $DynamicFieldBackendObject->SearchSQLGet(
463                    DynamicFieldConfig => $DynamicField,
464                    TableAlias         => "dfv$DynamicFieldJoinCounter",
465                    Operator           => $Operator,
466                    SearchTerm         => $Text,
467                );
468
469                $Counter++;
470            }
471            $SQLDynamicFieldWhereSub .= ') ';
472
473            if ($Counter) {
474                $SQLDynamicFieldWhere .= $SQLDynamicFieldWhereSub;
475                $NeedJoin = 1;
476            }
477        }
478
479        if ($NeedJoin) {
480            $SQLDynamicFieldFrom .= "
481                INNER JOIN dynamic_field_value dfv$DynamicFieldJoinCounter
482                    ON (df_obj_id_name.object_id = dfv$DynamicFieldJoinCounter.object_id
483                        AND dfv$DynamicFieldJoinCounter.field_id = "
484                . $DBObject->Quote( $DynamicField->{ID}, 'Integer' ) . ")
485            ";
486
487            $DynamicFieldJoinCounter++;
488        }
489    }
490
491    # Execute a dynamic field search, if a dynamic field where statement exists.
492    if ( $SQLDynamicFieldFrom && $SQLDynamicFieldWhere ) {
493
494        my @DynamicFieldCustomerIDs;
495
496        # Sql uery for the dynamic fields.
497        my $SQLDynamicField
498            = "SELECT DISTINCT(df_obj_id_name.object_name) FROM dynamic_field_obj_id_name df_obj_id_name "
499            . $SQLDynamicFieldFrom
500            . " WHERE "
501            . $SQLDynamicFieldWhere;
502
503        my $UsedCache;
504
505        if ( $Self->{CacheObject} ) {
506
507            my $DynamicFieldSearchCacheData = $Self->{CacheObject}->Get(
508                Type => $Self->{CacheType} . '_CustomerSearchDetailDynamicFields',
509                Key  => $SQLDynamicField,
510            );
511
512            if ( defined $DynamicFieldSearchCacheData ) {
513                if ( ref $DynamicFieldSearchCacheData eq 'ARRAY' ) {
514                    @DynamicFieldCustomerIDs = @{$DynamicFieldSearchCacheData};
515
516                    # Set the used cache flag.
517                    $UsedCache = 1;
518                }
519                else {
520                    $Kernel::OM->Get('Kernel::System::Log')->Log(
521                        Priority => 'error',
522                        Message  => 'Invalid ref ' . ref($DynamicFieldSearchCacheData) . '!'
523                    );
524                    return;
525                }
526            }
527        }
528
529        # Get the data only from database, if no cache entry exists.
530        if ( !$UsedCache ) {
531
532            return if !$DBObject->Prepare(
533                SQL => $SQLDynamicField,
534            );
535
536            while ( my @Row = $DBObject->FetchrowArray() ) {
537                push @DynamicFieldCustomerIDs, $Row[0];
538            }
539
540            if ( $Self->{CacheObject} ) {
541                $Self->{CacheObject}->Set(
542                    Type  => $Self->{CacheType} . '_CustomerSearchDetailDynamicFields',
543                    Key   => $SQLDynamicField,
544                    Value => \@DynamicFieldCustomerIDs,
545                    TTL   => $Self->{CustomerCompanyMap}->{CacheTTL},
546                );
547            }
548        }
549
550        # Add the user logins from the dynamic fields, if a search result exists from the dynamic field search
551        #   or skip the search and return a emptry array ref (or zero for the result 'COUNT', if no user logins exists
552        #   from the dynamic field search.
553        if (@DynamicFieldCustomerIDs) {
554
555            my $SQLQueryInCondition = $Kernel::OM->Get('Kernel::System::DB')->QueryInCondition(
556                Key      => $Self->{CustomerCompanyKey},
557                Values   => \@DynamicFieldCustomerIDs,
558                BindMode => 0,
559            );
560
561            push @SQLWhere, $SQLQueryInCondition;
562        }
563        else {
564            return $Result eq 'COUNT' ? 0 : [];
565        }
566    }
567
568    FIELD:
569    for my $Field (@ArraySearchFields) {
570
571        next FIELD if !@{ $Param{ $Field->{Name} } };
572
573        my $SQLQueryInCondition = $Kernel::OM->Get('Kernel::System::DB')->QueryInCondition(
574            Key      => $Field->{DatabaseField},
575            Values   => $Param{ $Field->{Name} },
576            BindMode => 0,
577        );
578
579        push @SQLWhere, $SQLQueryInCondition;
580    }
581
582    # Add the valid option if needed.
583    if ( $Self->{CustomerCompanyMap}->{CustomerValid} && $Valid ) {
584
585        my $ValidObject = $Kernel::OM->Get('Kernel::System::Valid');
586
587        push @SQLWhere,
588            "$Self->{CustomerCompanyMap}->{CustomerValid} IN (" . join( ', ', $ValidObject->ValidIDsGet() ) . ") ";
589    }
590
591    # Check if OrderBy contains only unique valid values.
592    my %OrderBySeen;
593    for my $OrderBy ( @{ $Param{OrderBy} } ) {
594
595        if ( !$OrderBy || $OrderBySeen{$OrderBy} ) {
596
597            $Kernel::OM->Get('Kernel::System::Log')->Log(
598                Priority => 'error',
599                Message  => "OrderBy contains invalid value '$OrderBy' "
600                    . 'or the value is used more than once!',
601            );
602            return;
603        }
604
605        # Remember the value to check if it appears more than once.
606        $OrderBySeen{$OrderBy} = 1;
607    }
608
609    # Check if OrderByDirection array contains only 'Up' or 'Down'.
610    DIRECTION:
611    for my $Direction ( @{ $Param{OrderByDirection} } ) {
612
613        # Only 'Up' or 'Down' allowed.
614        next DIRECTION if $Direction eq 'Up';
615        next DIRECTION if $Direction eq 'Down';
616
617        # found an error
618        $Kernel::OM->Get('Kernel::System::Log')->Log(
619            Priority => 'error',
620            Message  => "OrderByDirection can only contain 'Up' or 'Down'!",
621        );
622        return;
623    }
624
625    # Build the sql statement for the search.
626    my $SQL = "SELECT DISTINCT($Self->{CustomerCompanyKey})";
627
628    # Modify SQL when the result type is 'COUNT'.
629    if ( $Result eq 'COUNT' ) {
630        $SQL = "SELECT COUNT(DISTINCT($Self->{CustomerCompanyKey}))";
631    }
632
633    my @SQLOrderBy;
634
635    # The Order by clause is not needed for the result type 'COUNT'.
636    if ( $Result ne 'COUNT' ) {
637
638        my $Count = 0;
639
640        ORDERBY:
641        for my $OrderBy ( @{ $Param{OrderBy} } ) {
642
643            # Set the default order direction.
644            my $Direction = 'DESC';
645
646            # Add the given order direction.
647            if ( $Param{OrderByDirection}->[$Count] ) {
648                if ( $Param{OrderByDirection}->[$Count] eq 'Up' ) {
649                    $Direction = 'ASC';
650                }
651                elsif ( $Param{OrderByDirection}->[$Count] eq 'Down' ) {
652                    $Direction = 'DESC';
653                }
654            }
655
656            $Count++;
657
658            next ORDERBY if !$OrderByTable{$OrderBy};
659
660            push @SQLOrderBy, "$OrderByTable{$OrderBy} $Direction";
661
662            next ORDERBY if $OrderBy eq 'CustomerID';
663
664            $SQL .= ", $OrderByTable{$OrderBy}";
665        }
666
667        # If there is a possibility that the ordering is not determined
668        #   we add an descending ordering by id.
669        if ( !grep { $_ eq 'CustomerID' } ( @{ $Param{OrderBy} } ) ) {
670            push @SQLOrderBy, "$Self->{CustomerCompanyKey} DESC";
671        }
672    }
673
674    # Add form to the SQL after the order by creation.
675    $SQL .= " FROM $Self->{CustomerCompanyTable} ";
676
677    if (@SQLWhere) {
678        my $SQLWhereString = join ' AND ', map {"( $_ )"} @SQLWhere;
679        $SQL .= "WHERE $SQLWhereString ";
680    }
681
682    if (@SQLOrderBy) {
683        my $OrderByString = join ', ', @SQLOrderBy;
684        $SQL .= "ORDER BY $OrderByString";
685    }
686
687    # Check if a cache exists before we ask the database.
688    if ( $Self->{CacheObject} ) {
689
690        my $CacheData = $Kernel::OM->Get('Kernel::System::Cache')->Get(
691            Type => $Self->{CacheType} . '_CustomerCompanySearchDetail',
692            Key  => $SQL . $Param{Limit},
693        );
694
695        if ( defined $CacheData ) {
696            if ( ref $CacheData eq 'ARRAY' ) {
697                return $CacheData;
698            }
699            elsif ( ref $CacheData eq '' ) {
700                return $CacheData;
701            }
702            $Kernel::OM->Get('Kernel::System::Log')->Log(
703                Priority => 'error',
704                Message  => 'Invalid ref ' . ref($CacheData) . '!'
705            );
706            return;
707        }
708    }
709
710    return if !$DBObject->Prepare(
711        SQL   => $SQL,
712        Limit => $Param{Limit},
713    );
714
715    my @IDs;
716    while ( my @Row = $DBObject->FetchrowArray() ) {
717        push @IDs, $Row[0];
718    }
719
720    # Handle the diffrent result types.
721    if ( $Result eq 'COUNT' ) {
722
723        if ( $Self->{CacheObject} ) {
724            $Kernel::OM->Get('Kernel::System::Cache')->Set(
725                Type  => $Self->{CacheType} . '_CustomerCompanySearchDetail',
726                Key   => $SQL . $Param{Limit},
727                Value => $IDs[0],
728                TTL   => $Self->{CacheTTL},
729            );
730        }
731
732        return $IDs[0];
733    }
734
735    else {
736
737        if ( $Self->{CacheObject} ) {
738            $Kernel::OM->Get('Kernel::System::Cache')->Set(
739                Type  => $Self->{CacheType} . '_CustomerCompanySearchDetail',
740                Key   => $SQL . $Param{Limit},
741                Value => \@IDs,
742                TTL   => $Self->{CacheTTL},
743            );
744        }
745
746        return \@IDs;
747    }
748}
749
750sub CustomerCompanyGet {
751    my ( $Self, %Param ) = @_;
752
753    # check needed stuff
754    if ( !$Param{CustomerID} ) {
755        $Kernel::OM->Get('Kernel::System::Log')->Log(
756            Priority => 'error',
757            Message  => 'Need CustomerID!'
758        );
759        return;
760    }
761
762    # check cache
763    if ( $Self->{CacheObject} ) {
764        my $Data = $Self->{CacheObject}->Get(
765            Type => $Self->{CacheType},
766            Key  => "CustomerCompanyGet::$Param{CustomerID}",
767        );
768        return %{$Data} if ref $Data eq 'HASH';
769    }
770
771    # build select
772    my @Fields;
773    my %FieldsMap;
774
775    ENTRY:
776    for my $Entry ( @{ $Self->{CustomerCompanyMap}->{Map} } ) {
777        next ENTRY if $Entry->[5] eq 'dynamic_field';
778        push @Fields, $Entry->[2];
779        $FieldsMap{ $Entry->[2] } = $Entry->[0];
780    }
781    my $SQL = 'SELECT ' . join( ', ', @Fields );
782
783    if ( !$Self->{ForeignDB} ) {
784        $SQL .= ", create_time, create_by, change_time, change_by";
785    }
786
787    # this seems to be legacy, if Name is passed it should take precedence over CustomerID
788    my $CustomerID = $Param{Name} || $Param{CustomerID};
789
790    $SQL .= " FROM $Self->{CustomerCompanyTable} WHERE ";
791
792    if ( $Self->{CaseSensitive} ) {
793        $SQL .= "$Self->{CustomerCompanyKey} = ?";
794    }
795    else {
796        $SQL .= "LOWER($Self->{CustomerCompanyKey}) = LOWER( ? )";
797    }
798
799    # get initial data
800    return if !$Self->{DBObject}->Prepare(
801        SQL  => $SQL,
802        Bind => [ \$CustomerID ]
803    );
804
805    # fetch the result
806    my %Data;
807    ROW:
808    while ( my @Row = $Self->{DBObject}->FetchrowArray() ) {
809
810        my $MapCounter = 0;
811
812        for my $Field (@Fields) {
813            $Data{ $FieldsMap{$Field} } = $Row[$MapCounter];
814            $MapCounter++;
815        }
816
817        next ROW if $Self->{ForeignDB};
818
819        for my $Key (qw(CreateTime CreateBy ChangeTime ChangeBy)) {
820            $Data{$Key} = $Row[$MapCounter];
821            $MapCounter++;
822        }
823    }
824
825    # cache request
826    if ( $Self->{CacheObject} ) {
827        $Self->{CacheObject}->Set(
828            Type  => $Self->{CacheType},
829            Key   => "CustomerCompanyGet::$Param{CustomerID}",
830            Value => \%Data,
831            TTL   => $Self->{CacheTTL},
832        );
833    }
834
835    # return data
836    return (%Data);
837}
838
839sub CustomerCompanyAdd {
840    my ( $Self, %Param ) = @_;
841
842    # check ro/rw
843    if ( $Self->{ReadOnly} ) {
844        $Kernel::OM->Get('Kernel::System::Log')->Log(
845            Priority => 'error',
846            Message  => 'CustomerCompany backend is read only!'
847        );
848        return;
849    }
850
851    my @Fields;
852    my @Placeholders;
853    my @Values;
854
855    ENTRY:
856    for my $Entry ( @{ $Self->{CustomerCompanyMap}->{Map} } ) {
857
858        # ignore dynamic fields here
859        next ENTRY if $Entry->[5] eq 'dynamic_field';
860
861        push @Fields,       $Entry->[2];
862        push @Placeholders, '?';
863        push @Values,       \$Param{ $Entry->[0] };
864    }
865    if ( !$Self->{ForeignDB} ) {
866        push @Fields,       qw(create_time create_by change_time change_by);
867        push @Placeholders, qw(current_timestamp ? current_timestamp ?);
868        push @Values, ( \$Param{UserID}, \$Param{UserID} );
869    }
870
871    # build insert
872    my $SQL = "INSERT INTO $Self->{CustomerCompanyTable} (";
873    $SQL .= join( ', ', @Fields ) . " ) VALUES ( " . join( ', ', @Placeholders ) . " )";
874
875    return if !$Self->{DBObject}->Do(
876        SQL  => $SQL,
877        Bind => \@Values,
878    );
879
880    # log notice
881    $Kernel::OM->Get('Kernel::System::Log')->Log(
882        Priority => 'info',
883        Message =>
884            "CustomerCompany: '$Param{CustomerCompanyName}/$Param{CustomerID}' created successfully ($Param{UserID})!",
885    );
886
887    $Self->_CustomerCompanyCacheClear( CustomerID => $Param{CustomerID} );
888
889    return $Param{CustomerID};
890}
891
892sub CustomerCompanyUpdate {
893    my ( $Self, %Param ) = @_;
894
895    # check ro/rw
896    if ( $Self->{ReadOnly} ) {
897        $Kernel::OM->Get('Kernel::System::Log')->Log(
898            Priority => 'error',
899            Message  => 'Customer backend is read only!'
900        );
901        return;
902    }
903
904    # check needed stuff
905    for my $Entry ( @{ $Self->{CustomerCompanyMap}->{Map} } ) {
906        if (
907            !$Param{ $Entry->[0] }
908            && $Entry->[5] ne 'dynamic_field'    # ignore dynamic fields here
909            && $Entry->[4]
910            && $Entry->[0] ne 'UserPassword'
911            )
912        {
913            $Kernel::OM->Get('Kernel::System::Log')->Log(
914                Priority => 'error',
915                Message  => "Need $Entry->[0]!"
916            );
917            return;
918        }
919    }
920
921    my @Fields;
922    my @Values;
923
924    FIELD:
925    for my $Entry ( @{ $Self->{CustomerCompanyMap}->{Map} } ) {
926        next FIELD if $Entry->[0] =~ /^UserPassword$/i;
927        next FIELD if $Entry->[5] eq 'dynamic_field';     # skip dynamic fields
928        push @Fields, $Entry->[2] . ' = ?';
929        push @Values, \$Param{ $Entry->[0] };
930    }
931    if ( !$Self->{ForeignDB} ) {
932        push @Fields, ( 'change_time = current_timestamp', 'change_by = ?' );
933        push @Values, \$Param{UserID};
934    }
935
936    # create SQL statement
937    my $SQL = "UPDATE $Self->{CustomerCompanyTable} SET ";
938    $SQL .= join( ', ', @Fields );
939
940    if ( $Self->{CaseSensitive} ) {
941        $SQL .= " WHERE $Self->{CustomerCompanyKey} = ?";
942    }
943    else {
944        $SQL .= " WHERE LOWER($Self->{CustomerCompanyKey}) = LOWER( ? )";
945    }
946    push @Values, \$Param{CustomerCompanyID};
947
948    return if !$Self->{DBObject}->Do(
949        SQL  => $SQL,
950        Bind => \@Values,
951    );
952
953    # log notice
954    $Kernel::OM->Get('Kernel::System::Log')->Log(
955        Priority => 'info',
956        Message =>
957            "CustomerCompany: '$Param{CustomerCompanyName}/$Param{CustomerID}' updated successfully ($Param{UserID})!",
958    );
959
960    $Self->_CustomerCompanyCacheClear( CustomerID => $Param{CustomerID} );
961    if ( $Param{CustomerCompanyID} ne $Param{CustomerID} ) {
962        $Self->_CustomerCompanyCacheClear( CustomerID => $Param{CustomerCompanyID} );
963    }
964
965    return 1;
966}
967
968sub _CustomerCompanyCacheClear {
969    my ( $Self, %Param ) = @_;
970
971    return if !$Self->{CacheObject};
972
973    if ( !$Param{CustomerID} ) {
974        $Kernel::OM->Get('Kernel::System::Log')->Log(
975            Priority => 'error',
976            Message  => 'Need CustomerID!'
977        );
978        return;
979    }
980
981    $Self->{CacheObject}->Delete(
982        Type => $Self->{CacheType},
983        Key  => "CustomerCompanyGet::$Param{CustomerID}",
984    );
985
986    # delete all search cache entries
987    $Self->{CacheObject}->CleanUp(
988        Type => $Self->{CacheType} . '_CustomerCompanyList',
989    );
990
991    for my $Function (qw(CustomerCompanyList)) {
992        for my $Valid ( 0 .. 1 ) {
993            $Self->{CacheObject}->Delete(
994                Type => $Self->{CacheType},
995                Key  => "${Function}::${Valid}",
996            );
997        }
998    }
999
1000    return 1;
1001}
1002
1003sub DESTROY {
1004    my $Self = shift;
1005
1006    # disconnect if it's not a parent DBObject
1007    if ( $Self->{NotParentDBObject} ) {
1008        if ( $Self->{DBObject} ) {
1009            $Self->{DBObject}->Disconnect();
1010        }
1011    }
1012
1013    return 1;
1014}
1015
10161;
1017