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::GenericInterface::Operation::Ticket::TicketCreate;
10
11use strict;
12use warnings;
13
14use Kernel::System::VariableCheck qw(IsArrayRefWithData IsHashRefWithData IsString IsStringWithData);
15
16use parent qw(
17    Kernel::GenericInterface::Operation::Common
18    Kernel::GenericInterface::Operation::Ticket::Common
19);
20
21our $ObjectManagerDisabled = 1;
22
23=head1 NAME
24
25Kernel::GenericInterface::Operation::Ticket::TicketCreate - GenericInterface Ticket TicketCreate Operation backend
26
27=head1 PUBLIC INTERFACE
28
29=head2 new()
30
31usually, you want to create an instance of this
32by using Kernel::GenericInterface::Operation->new();
33
34=cut
35
36sub new {
37    my ( $Type, %Param ) = @_;
38
39    my $Self = {};
40    bless( $Self, $Type );
41
42    # check needed objects
43    for my $Needed (qw( DebuggerObject WebserviceID )) {
44        if ( !$Param{$Needed} ) {
45            return {
46                Success      => 0,
47                ErrorMessage => "Got no $Needed!",
48            };
49        }
50
51        $Self->{$Needed} = $Param{$Needed};
52    }
53
54    $Self->{Config}    = $Kernel::OM->Get('Kernel::Config')->Get('GenericInterface::Operation::TicketCreate');
55    $Self->{Operation} = $Param{Operation};
56
57    return $Self;
58}
59
60=head2 Run()
61
62perform TicketCreate Operation. This will return the created ticket number.
63
64    my $Result = $OperationObject->Run(
65        Data => {
66            UserLogin         => 'some agent login',                            # UserLogin or CustomerUserLogin or SessionID is
67                                                                                #   required
68            CustomerUserLogin => 'some customer login',
69            SessionID         => 123,
70
71            Password  => 'some password',                                       # if UserLogin or CustomerUserLogin is sent then
72                                                                                #   Password is required
73
74            Ticket => {
75                Title      => 'some ticket title',
76
77                QueueID       => 123,                                           # QueueID or Queue is required
78                Queue         => 'some queue name',
79
80                LockID        => 123,                                           # optional
81                Lock          => 'some lock name',                              # optional
82                TypeID        => 123,                                           # optional
83                Type          => 'some type name',                              # optional
84                ServiceID     => 123,                                           # optional
85                Service       => 'some service name',                           # optional
86                SLAID         => 123,                                           # optional
87                SLA           => 'some SLA name',                               # optional
88
89                StateID       => 123,                                           # StateID or State is required
90                State         => 'some state name',
91
92                PriorityID    => 123,                                           # PriorityID or Priority is required
93                Priority      => 'some priority name',
94
95                OwnerID       => 123,                                           # optional
96                Owner         => 'some user login',                             # optional
97                ResponsibleID => 123,                                           # optional
98                Responsible   => 'some user login',                             # optional
99                CustomerUser  => 'some customer user login',
100
101                PendingTime {       # optional
102                    Year   => 2011,
103                    Month  => 12
104                    Day    => 03,
105                    Hour   => 23,
106                    Minute => 05,
107                },
108                # or
109                # PendingTime {
110                #     Diff => 10080, # Pending time in minutes
111                #},
112            },
113            Article => {
114                CommunicationChannel            => 'Email',                    # CommunicationChannel or CommunicationChannelID must be provided.
115                CommunicationChannelID          => 1,
116                IsVisibleForCustomer            => 1,                          # optional
117                SenderTypeID                    => 123,                        # optional
118                SenderType                      => 'some sender type name',    # optional
119                AutoResponseType                => 'some auto response type',  # optional
120                From                            => 'some from string',         # optional
121                Subject                         => 'some subject',
122                Body                            => 'some body',
123                ContentType                     => 'some content type',        # ContentType or MimeType and Charset is required
124                MimeType                        => 'some mime type',
125                Charset                         => 'some charset',
126                HistoryType                     => 'some history type',        # optional
127                HistoryComment                  => 'Some  history comment',    # optional
128                TimeUnit                        => 123,                        # optional
129                NoAgentNotify                   => 1,                          # optional
130                ForceNotificationToUserID       => [1, 2, 3]                   # optional
131                ExcludeNotificationToUserID     => [1, 2, 3]                   # optional
132                ExcludeMuteNotificationToUserID => [1, 2, 3]                   # optional
133            },
134
135            DynamicField => [                                                  # optional
136                {
137                    Name   => 'some name',
138                    Value  => $Value,                                          # value type depends on the dynamic field
139                },
140                # ...
141            ],
142            # or
143            # DynamicField => {
144            #    Name   => 'some name',
145            #    Value  => $Value,
146            #},
147
148            Attachment => [
149                {
150                    Content     => 'content'                                 # base64 encoded
151                    ContentType => 'some content type'
152                    Filename    => 'some fine name'
153                },
154                # ...
155            ],
156            #or
157            #Attachment => {
158            #    Content     => 'content'
159            #    ContentType => 'some content type'
160            #    Filename    => 'some fine name'
161            #},
162        },
163    );
164
165    $Result = {
166        Success         => 1,                       # 0 or 1
167        ErrorMessage    => '',                      # in case of error
168        Data            => {                        # result data payload after Operation
169            TicketID    => 123,                     # Ticket  ID number in OTRS (help desk system)
170            TicketNumber => 2324454323322           # Ticket Number in OTRS (Help desk system)
171            ArticleID   => 43,                      # Article ID number in OTRS (help desk system)
172            Error => {                              # should not return errors
173                    ErrorCode    => 'Ticket.Create.ErrorCode'
174                    ErrorMessage => 'Error Description'
175            },
176
177            # If IncludeTicketData is enabled
178            Ticket => [
179                {
180                    TicketNumber       => '20101027000001',
181                    Title              => 'some title',
182                    TicketID           => 123,
183                    State              => 'some state',
184                    StateID            => 123,
185                    StateType          => 'some state type',
186                    Priority           => 'some priority',
187                    PriorityID         => 123,
188                    Lock               => 'lock',
189                    LockID             => 123,
190                    Queue              => 'some queue',
191                    QueueID            => 123,
192                    CustomerID         => 'customer_id_123',
193                    CustomerUserID     => 'customer_user_id_123',
194                    Owner              => 'some_owner_login',
195                    OwnerID            => 123,
196                    Type               => 'some ticket type',
197                    TypeID             => 123,
198                    SLA                => 'some sla',
199                    SLAID              => 123,
200                    Service            => 'some service',
201                    ServiceID          => 123,
202                    Responsible        => 'some_responsible_login',
203                    ResponsibleID      => 123,
204                    Age                => 3456,
205                    Created            => '2010-10-27 20:15:00'
206                    CreateBy           => 123,
207                    Changed            => '2010-10-27 20:15:15',
208                    ChangeBy           => 123,
209                    ArchiveFlag        => 'y',
210
211                    DynamicField => [
212                        {
213                            Name  => 'some name',
214                            Value => 'some value',
215                        },
216                    ],
217
218                    # (time stamps of expected escalations)
219                    EscalationResponseTime           (unix time stamp of response time escalation)
220                    EscalationUpdateTime             (unix time stamp of update time escalation)
221                    EscalationSolutionTime           (unix time stamp of solution time escalation)
222
223                    # (general escalation info of nearest escalation type)
224                    EscalationDestinationIn          (escalation in e. g. 1h 4m)
225                    EscalationDestinationTime        (date of escalation in unix time, e. g. 72193292)
226                    EscalationDestinationDate        (date of escalation, e. g. "2009-02-14 18:00:00")
227                    EscalationTimeWorkingTime        (seconds of working/service time till escalation, e. g. "1800")
228                    EscalationTime                   (seconds total till escalation of nearest escalation time type - response, update or solution time, e. g. "3600")
229
230                    # (detailed escalation info about first response, update and solution time)
231                    FirstResponseTimeEscalation      (if true, ticket is escalated)
232                    FirstResponseTimeNotification    (if true, notify - x% of escalation has reached)
233                    FirstResponseTimeDestinationTime (date of escalation in unix time, e. g. 72193292)
234                    FirstResponseTimeDestinationDate (date of escalation, e. g. "2009-02-14 18:00:00")
235                    FirstResponseTimeWorkingTime     (seconds of working/service time till escalation, e. g. "1800")
236                    FirstResponseTime                (seconds total till escalation, e. g. "3600")
237
238                    UpdateTimeEscalation             (if true, ticket is escalated)
239                    UpdateTimeNotification           (if true, notify - x% of escalation has reached)
240                    UpdateTimeDestinationTime        (date of escalation in unix time, e. g. 72193292)
241                    UpdateTimeDestinationDate        (date of escalation, e. g. "2009-02-14 18:00:00")
242                    UpdateTimeWorkingTime            (seconds of working/service time till escalation, e. g. "1800")
243                    UpdateTime                       (seconds total till escalation, e. g. "3600")
244
245                    SolutionTimeEscalation           (if true, ticket is escalated)
246                    SolutionTimeNotification         (if true, notify - x% of escalation has reached)
247                    SolutionTimeDestinationTime      (date of escalation in unix time, e. g. 72193292)
248                    SolutionTimeDestinationDate      (date of escalation, e. g. "2009-02-14 18:00:00")
249                    SolutionTimeWorkingTime          (seconds of working/service time till escalation, e. g. "1800")
250                    SolutionTime                     (seconds total till escalation, e. g. "3600")
251
252                    Article => [
253                        {
254                            ArticleID
255                            From
256                            To
257                            Cc
258                            Subject
259                            Body
260                            ReplyTo
261                            MessageID
262                            InReplyTo
263                            References
264                            SenderType
265                            SenderTypeID
266                            CommunicationChannelID
267                            IsVisibleForCustomer
268                            ContentType
269                            Charset
270                            MimeType
271                            IncomingTime
272
273                            DynamicField => [
274                                {
275                                    Name  => 'some name',
276                                    Value => 'some value',
277                                },
278                            ],
279
280                            Attachment => [
281                                {
282                                    Content            => "xxxx",     # actual attachment contents, base64 enconded
283                                    ContentAlternative => "",
284                                    ContentID          => "",
285                                    ContentType        => "application/pdf",
286                                    Filename           => "StdAttachment-Test1.pdf",
287                                    Filesize           => "4.6 KBytes",
288                                    FilesizeRaw        => 4722,
289                                },
290                            ],
291                        },
292                    ],
293                },
294            ],
295        },
296    };
297
298=cut
299
300sub Run {
301    my ( $Self, %Param ) = @_;
302
303    my $Result = $Self->Init(
304        WebserviceID => $Self->{WebserviceID},
305    );
306
307    if ( !$Result->{Success} ) {
308        $Self->ReturnError(
309            ErrorCode    => 'Webservice.InvalidConfiguration',
310            ErrorMessage => $Result->{ErrorMessage},
311        );
312    }
313
314    # check needed stuff
315    if (
316        !$Param{Data}->{UserLogin}
317        && !$Param{Data}->{CustomerUserLogin}
318        && !$Param{Data}->{SessionID}
319        )
320    {
321        return $Self->ReturnError(
322            ErrorCode    => 'TicketCreate.MissingParameter',
323            ErrorMessage => "TicketCreate: UserLogin, CustomerUserLogin or SessionID is required!",
324        );
325    }
326
327    if ( $Param{Data}->{UserLogin} || $Param{Data}->{CustomerUserLogin} ) {
328
329        if ( !$Param{Data}->{Password} )
330        {
331            return $Self->ReturnError(
332                ErrorCode    => 'TicketCreate.MissingParameter',
333                ErrorMessage => "TicketCreate: Password or SessionID is required!",
334            );
335        }
336    }
337
338    # authenticate user
339    my ( $UserID, $UserType ) = $Self->Auth(
340        %Param,
341    );
342
343    if ( !$UserID ) {
344        return $Self->ReturnError(
345            ErrorCode    => 'TicketCreate.AuthFail',
346            ErrorMessage => "TicketCreate: User could not be authenticated!",
347        );
348    }
349
350    my $PermissionUserID = $UserID;
351    if ( $UserType eq 'Customer' ) {
352        $UserID = $Kernel::OM->Get('Kernel::Config')->Get('CustomerPanelUserID');
353    }
354
355    # check needed hashes
356    for my $Needed (qw(Ticket Article)) {
357        if ( !IsHashRefWithData( $Param{Data}->{$Needed} ) ) {
358            return $Self->ReturnError(
359                ErrorCode    => 'TicketCreate.MissingParameter',
360                ErrorMessage => "TicketCreate: $Needed parameter is missing or not valid!",
361            );
362        }
363    }
364
365    # check optional array/hashes
366    for my $Optional (qw(DynamicField Attachment)) {
367        if (
368            defined $Param{Data}->{$Optional}
369            && !IsHashRefWithData( $Param{Data}->{$Optional} )
370            && !IsArrayRefWithData( $Param{Data}->{$Optional} )
371            )
372        {
373            return $Self->ReturnError(
374                ErrorCode    => 'TicketCreate.MissingParameter',
375                ErrorMessage => "TicketCreate: $Optional parameter is missing or not valid!",
376            );
377        }
378    }
379
380    # isolate ticket parameter
381    my $Ticket = $Param{Data}->{Ticket};
382
383    # remove leading and trailing spaces
384    for my $Attribute ( sort keys %{$Ticket} ) {
385        if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) {
386
387            #remove leading spaces
388            $Ticket->{$Attribute} =~ s{\A\s+}{};
389
390            #remove trailing spaces
391            $Ticket->{$Attribute} =~ s{\s+\z}{};
392        }
393    }
394    if ( IsHashRefWithData( $Ticket->{PendingTime} ) ) {
395        for my $Attribute ( sort keys %{ $Ticket->{PendingTime} } ) {
396            if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) {
397
398                #remove leading spaces
399                $Ticket->{PendingTime}->{$Attribute} =~ s{\A\s+}{};
400
401                #remove trailing spaces
402                $Ticket->{PendingTime}->{$Attribute} =~ s{\s+\z}{};
403            }
404        }
405    }
406
407    # check Ticket attribute values
408    my $TicketCheck = $Self->_CheckTicket( Ticket => $Ticket );
409
410    if ( !$TicketCheck->{Success} ) {
411        return $Self->ReturnError( %{$TicketCheck} );
412    }
413
414    # check create permissions
415    my $Permission = $Self->CheckCreatePermissions(
416        Ticket   => $Ticket,
417        UserID   => $PermissionUserID,
418        UserType => $UserType,
419    );
420
421    if ( !$Permission ) {
422        return $Self->ReturnError(
423            ErrorCode    => 'TicketCreate.AccessDenied',
424            ErrorMessage => "TicketCreate: Can not create tickets in given Queue or QueueID!",
425        );
426    }
427
428    # isolate Article parameter
429    my $Article = $Param{Data}->{Article};
430    $Article->{UserType} = $UserType;
431
432    # remove leading and trailing spaces
433    for my $Attribute ( sort keys %{$Article} ) {
434        if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) {
435
436            #remove leading spaces
437            $Article->{$Attribute} =~ s{\A\s+}{};
438
439            #remove trailing spaces
440            $Article->{$Attribute} =~ s{\s+\z}{};
441        }
442    }
443    if ( IsHashRefWithData( $Article->{OrigHeader} ) ) {
444        for my $Attribute ( sort keys %{ $Article->{OrigHeader} } ) {
445            if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) {
446
447                #remove leading spaces
448                $Article->{OrigHeader}->{$Attribute} =~ s{\A\s+}{};
449
450                #remove trailing spaces
451                $Article->{OrigHeader}->{$Attribute} =~ s{\s+\z}{};
452            }
453        }
454    }
455
456    # Check attributes that can be set by sysconfig.
457    if ( !$Article->{AutoResponseType} ) {
458        $Article->{AutoResponseType} = $Self->{Config}->{AutoResponseType} || '';
459    }
460
461    # TODO: GenericInterface::Operation::TicketCreate###CommunicationChannel
462    if ( !$Article->{CommunicationChannelID} && !$Article->{CommunicationChannel} ) {
463        $Article->{CommunicationChannel} = 'Internal';
464    }
465    if ( !defined $Article->{IsVisibleForCustomer} ) {
466        $Article->{IsVisibleForCustomer} = $Self->{Config}->{IsVisibleForCustomer} // 1;
467    }
468    if ( !$Article->{SenderTypeID} && !$Article->{SenderType} ) {
469        $Article->{SenderType} = $UserType eq 'User' ? 'agent' : 'customer';
470    }
471    if ( !$Article->{HistoryType} ) {
472        $Article->{HistoryType} = $Self->{Config}->{HistoryType} || '';
473    }
474    if ( !$Article->{HistoryComment} ) {
475        $Article->{HistoryComment} = $Self->{Config}->{HistoryComment} || '';
476    }
477
478    # check Article attribute values
479    my $ArticleCheck = $Self->_CheckArticle( Article => $Article );
480
481    if ( !$ArticleCheck->{Success} ) {
482        if ( !$ArticleCheck->{ErrorCode} ) {
483            return {
484                Success => 0,
485                %{$ArticleCheck},
486            };
487        }
488        return $Self->ReturnError( %{$ArticleCheck} );
489    }
490
491    my $DynamicField;
492    my @DynamicFieldList;
493
494    if ( defined $Param{Data}->{DynamicField} ) {
495
496        # isolate DynamicField parameter
497        $DynamicField = $Param{Data}->{DynamicField};
498
499        # homogenate input to array
500        if ( ref $DynamicField eq 'HASH' ) {
501            push @DynamicFieldList, $DynamicField;
502        }
503        else {
504            @DynamicFieldList = @{$DynamicField};
505        }
506
507        # check DynamicField internal structure
508        for my $DynamicFieldItem (@DynamicFieldList) {
509            if ( !IsHashRefWithData($DynamicFieldItem) ) {
510                return {
511                    ErrorCode => 'TicketCreate.InvalidParameter',
512                    ErrorMessage =>
513                        "TicketCreate: Ticket->DynamicField parameter is invalid!",
514                };
515            }
516
517            # remove leading and trailing spaces
518            for my $Attribute ( sort keys %{$DynamicFieldItem} ) {
519                if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) {
520
521                    #remove leading spaces
522                    $DynamicFieldItem->{$Attribute} =~ s{\A\s+}{};
523
524                    #remove trailing spaces
525                    $DynamicFieldItem->{$Attribute} =~ s{\s+\z}{};
526                }
527            }
528
529            # check DynamicField attribute values
530            my $DynamicFieldCheck = $Self->_CheckDynamicField( DynamicField => $DynamicFieldItem );
531
532            if ( !$DynamicFieldCheck->{Success} ) {
533                return $Self->ReturnError( %{$DynamicFieldCheck} );
534            }
535        }
536    }
537
538    my $Attachment;
539    my @AttachmentList;
540
541    if ( defined $Param{Data}->{Attachment} ) {
542
543        # isolate Attachment parameter
544        $Attachment = $Param{Data}->{Attachment};
545
546        # homogenate input to array
547        if ( ref $Attachment eq 'HASH' ) {
548            push @AttachmentList, $Attachment;
549        }
550        else {
551            @AttachmentList = @{$Attachment};
552        }
553
554        # check Attachment internal structure
555        for my $AttachmentItem (@AttachmentList) {
556            if ( !IsHashRefWithData($AttachmentItem) ) {
557                return {
558                    ErrorCode => 'TicketCreate.InvalidParameter',
559                    ErrorMessage =>
560                        "TicketCreate: Ticket->Attachment parameter is invalid!",
561                };
562            }
563
564            # remove leading and trailing spaces
565            for my $Attribute ( sort keys %{$AttachmentItem} ) {
566                if ( ref $Attribute ne 'HASH' && ref $Attribute ne 'ARRAY' ) {
567
568                    #remove leading spaces
569                    $AttachmentItem->{$Attribute} =~ s{\A\s+}{};
570
571                    #remove trailing spaces
572                    $AttachmentItem->{$Attribute} =~ s{\s+\z}{};
573                }
574            }
575
576            # check Attachment attribute values
577            my $AttachmentCheck = $Self->_CheckAttachment( Attachment => $AttachmentItem );
578
579            if ( !$AttachmentCheck->{Success} ) {
580                return $Self->ReturnError( %{$AttachmentCheck} );
581            }
582        }
583    }
584
585    return $Self->_TicketCreate(
586        Ticket           => $Ticket,
587        Article          => $Article,
588        DynamicFieldList => \@DynamicFieldList,
589        AttachmentList   => \@AttachmentList,
590        UserID           => $UserID,
591    );
592}
593
594=begin Internal:
595
596=head2 _CheckTicket()
597
598checks if the given ticket parameters are valid.
599
600    my $TicketCheck = $OperationObject->_CheckTicket(
601        Ticket => $Ticket,                          # all ticket parameters
602    );
603
604    returns:
605
606    $TicketCheck = {
607        Success => 1,                               # if everything is OK
608    }
609
610    $TicketCheck = {
611        ErrorCode    => 'Function.Error',           # if error
612        ErrorMessage => 'Error description',
613    }
614
615=cut
616
617sub _CheckTicket {
618    my ( $Self, %Param ) = @_;
619
620    my $Ticket = $Param{Ticket};
621
622    # check ticket internally
623    for my $Needed (qw(Title CustomerUser)) {
624        if ( !$Ticket->{$Needed} ) {
625            return {
626                ErrorCode    => 'TicketCreate.MissingParameter',
627                ErrorMessage => "TicketCreate: Ticket->$Needed parameter is missing!",
628            };
629        }
630    }
631
632    # check Ticket->CustomerUser
633    if ( !$Self->ValidateCustomer( %{$Ticket} ) ) {
634        return {
635            ErrorCode => 'TicketCreate.InvalidParameter',
636            ErrorMessage =>
637                "TicketCreate: Ticket->CustomerUser parameter is invalid!",
638        };
639    }
640
641    # check Ticket->Queue
642    if ( !$Ticket->{QueueID} && !$Ticket->{Queue} ) {
643        return {
644            ErrorCode    => 'TicketCreate.MissingParameter',
645            ErrorMessage => "TicketCreate: Ticket->QueueID or Ticket->Queue parameter is required!",
646        };
647    }
648    if ( !$Self->ValidateQueue( %{$Ticket} ) ) {
649        return {
650            ErrorCode    => 'TicketCreate.InvalidParameter',
651            ErrorMessage => "TicketCreate: Ticket->QueueID or Ticket->Queue parameter is invalid!",
652        };
653    }
654
655    # check Ticket->Lock
656    if ( $Ticket->{LockID} || $Ticket->{Lock} ) {
657        if ( !$Self->ValidateLock( %{$Ticket} ) ) {
658            return {
659                ErrorCode    => 'TicketCreate.InvalidParameter',
660                ErrorMessage => "TicketCreate: Ticket->LockID or Ticket->Lock parameter is"
661                    . " invalid!",
662            };
663        }
664    }
665
666    # check Ticket->Type
667    # Ticket type could be required or not depending on sysconfig option
668    if (
669        !$Ticket->{TypeID}
670        && !$Ticket->{Type}
671        && $Kernel::OM->Get('Kernel::Config')->Get('Ticket::Type')
672        )
673    {
674        return {
675            ErrorCode    => 'TicketCreate.MissingParameter',
676            ErrorMessage => "TicketCreate: Ticket->TypeID or Ticket->Type parameter is required"
677                . " by sysconfig option!",
678        };
679    }
680    if ( $Ticket->{TypeID} || $Ticket->{Type} ) {
681        if ( !$Self->ValidateType( %{$Ticket} ) ) {
682            return {
683                ErrorCode => 'TicketCreate.InvalidParameter',
684                ErrorMessage =>
685                    "TicketCreate: Ticket->TypeID or Ticket->Type parameter is invalid!",
686            };
687        }
688    }
689
690    # check Ticket->Service
691    if ( $Ticket->{ServiceID} || $Ticket->{Service} ) {
692        if ( !$Self->ValidateService( %{$Ticket} ) ) {
693            return {
694                ErrorCode => 'TicketCreate.InvalidParameter',
695                ErrorMessage =>
696                    "TicketCreate: Ticket->ServiceID or Ticket->Service parameter is invalid!",
697            };
698        }
699    }
700
701    # check Ticket->SLA
702    if ( $Ticket->{SLAID} || $Ticket->{SLA} ) {
703        if ( !$Self->ValidateSLA( %{$Ticket} ) ) {
704            return {
705                ErrorCode => 'TicketCreate.InvalidParameter',
706                ErrorMessage =>
707                    "TicketCreate: Ticket->SLAID or Ticket->SLA parameter is invalid!",
708            };
709        }
710    }
711
712    # check Ticket->State
713    if ( !$Ticket->{StateID} && !$Ticket->{State} ) {
714        return {
715            ErrorCode    => 'TicketCreate.MissingParameter',
716            ErrorMessage => "TicketCreate: Ticket->StateID or Ticket->State parameter is required!",
717        };
718    }
719    if ( !$Self->ValidateState( %{$Ticket} ) ) {
720        return {
721            ErrorCode    => 'TicketCreate.InvalidParameter',
722            ErrorMessage => "TicketCreate: Ticket->StateID or Ticket->State parameter is invalid!",
723        };
724    }
725
726    # check Ticket->Priority
727    if ( !$Ticket->{PriorityID} && !$Ticket->{Priority} ) {
728        return {
729            ErrorCode    => 'TicketCreate.MissingParameter',
730            ErrorMessage => "TicketCreate: Ticket->PriorityID or Ticket->Priority parameter is"
731                . " required!",
732        };
733    }
734    if ( !$Self->ValidatePriority( %{$Ticket} ) ) {
735        return {
736            ErrorCode    => 'TicketCreate.InvalidParameter',
737            ErrorMessage => "TicketCreate: Ticket->PriorityID or Ticket->Priority parameter is"
738                . " invalid!",
739        };
740    }
741
742    # check Ticket->Owner
743    if ( $Ticket->{OwnerID} || $Ticket->{Owner} ) {
744        if ( !$Self->ValidateOwner( %{$Ticket} ) ) {
745            return {
746                ErrorCode => 'TicketCreate.InvalidParameter',
747                ErrorMessage =>
748                    "TicketCreate: Ticket->OwnerID or Ticket->Owner parameter is invalid!",
749            };
750        }
751    }
752
753    # check Ticket->Responsible
754    if ( $Ticket->{ResponsibleID} || $Ticket->{Responsible} ) {
755        if ( !$Self->ValidateResponsible( %{$Ticket} ) ) {
756            return {
757                ErrorCode    => 'TicketCreate.InvalidParameter',
758                ErrorMessage => "TicketCreate: Ticket->ResponsibleID or Ticket->Responsible"
759                    . " parameter is invalid!",
760            };
761        }
762    }
763
764    # check Ticket->PendingTime
765    if ( $Ticket->{PendingTime} ) {
766        if ( !$Self->ValidatePendingTime( %{$Ticket} ) ) {
767            return {
768                ErrorCode    => 'TicketCreate.InvalidParameter',
769                ErrorMessage => "TicketCreate: Ticket->PendingTime parameter is invalid!",
770            };
771        }
772    }
773
774    # if everything is OK then return Success
775    return {
776        Success => 1,
777    };
778}
779
780=head2 _CheckArticle()
781
782checks if the given article parameter is valid.
783
784    my $ArticleCheck = $OperationObject->_CheckArticle(
785        Article => $Article,                        # all article parameters
786    );
787
788    returns:
789
790    $ArticleCheck = {
791        Success => 1,                               # if everething is OK
792    }
793
794    $ArticleCheck = {
795        ErrorCode    => 'Function.Error',           # if error
796        ErrorMessage => 'Error description',
797    }
798
799=cut
800
801sub _CheckArticle {
802    my ( $Self, %Param ) = @_;
803
804    my $Article = $Param{Article};
805
806    # check ticket internally
807    for my $Needed (qw(Subject Body AutoResponseType)) {
808        if ( !$Article->{$Needed} ) {
809            return {
810                ErrorCode    => 'TicketCreate.MissingParameter',
811                ErrorMessage => "TicketCreate: Article->$Needed parameter is missing!",
812            };
813        }
814    }
815
816    # check Article->AutoResponseType
817    if ( !$Article->{AutoResponseType} ) {
818
819        # return internal server error
820        return {
821            ErrorMessage => "TicketCreate: Article->AutoResponseType parameter is required!"
822        };
823    }
824
825    if ( !$Self->ValidateAutoResponseType( %{$Article} ) ) {
826        return {
827            ErrorCode    => 'TicketCreate.InvalidParameter',
828            ErrorMessage => "TicketCreate: Article->AutoResponseType parameter is invalid!",
829        };
830    }
831
832    # check Article->CommunicationChannel
833    if ( !$Article->{CommunicationChannel} && !$Article->{CommunicationChannelID} ) {
834
835        # return internal server error
836        return {
837            ErrorMessage => "TicketCreate: Article->CommunicationChannelID or Article->CommunicationChannel parameter"
838                . " is required and Sysconfig CommunicationChannelID setting could not be read!"
839        };
840    }
841    if ( !$Self->ValidateArticleCommunicationChannel( %{$Article} ) ) {
842        return {
843            ErrorCode    => 'TicketCreate.InvalidParameter',
844            ErrorMessage => "TicketCreate: Article->CommunicationChannel or Article->CommunicationChannelID parameter"
845                . " is invalid or not supported!",
846        };
847    }
848
849    # check Article->SenderType
850    if ( !$Article->{SenderTypeID} && !$Article->{SenderType} ) {
851
852        # return internal server error
853        return {
854            ErrorMessage => "TicketCreate: Article->SenderTypeID or Article->SenderType parameter"
855                . " is required and Sysconfig SenderTypeID setting could not be read!"
856        };
857    }
858    if ( !$Self->ValidateSenderType( %{$Article} ) ) {
859        return {
860            ErrorCode    => 'TicketCreate.InvalidParameter',
861            ErrorMessage => "TicketCreate: Article->SenderTypeID or Ticket->SenderType parameter"
862                . " is invalid!",
863        };
864    }
865
866    # check Article->From
867    if ( $Article->{From} ) {
868        if ( !$Self->ValidateFrom( %{$Article} ) ) {
869            return {
870                ErrorCode    => 'TicketCreate.InvalidParameter',
871                ErrorMessage => "TicketCreate: Article->From parameter is invalid!",
872            };
873        }
874    }
875
876    # check Article->ContentType vs Article->MimeType and Article->Charset
877    if ( !$Article->{ContentType} && !$Article->{MimeType} && !$Article->{Charset} ) {
878        return {
879            ErrorCode    => 'TicketCreate.MissingParameter',
880            ErrorMessage => "TicketCreate: Article->ContentType or Ticket->MimeType and"
881                . " Article->Charset parameters are required!",
882        };
883    }
884
885    if ( $Article->{MimeType} && !$Article->{Charset} ) {
886        return {
887            ErrorCode    => 'TicketCreate.MissingParameter',
888            ErrorMessage => "TicketCreate: Article->Charset is required!",
889        };
890    }
891
892    if ( $Article->{Charset} && !$Article->{MimeType} ) {
893        return {
894            ErrorCode    => 'TicketCreate.MissingParameter',
895            ErrorMessage => "TicketCreate: Article->MimeType is required!",
896        };
897    }
898
899    # check Article->MimeType
900    if ( $Article->{MimeType} ) {
901
902        $Article->{MimeType} = lc $Article->{MimeType};
903
904        if ( !$Self->ValidateMimeType( %{$Article} ) ) {
905            return {
906                ErrorCode    => 'TicketCreate.InvalidParameter',
907                ErrorMessage => "TicketCreate: Article->MimeType is invalid!",
908            };
909        }
910    }
911
912    # check Article->MimeType
913    if ( $Article->{Charset} ) {
914
915        $Article->{Charset} = lc $Article->{Charset};
916
917        if ( !$Self->ValidateCharset( %{$Article} ) ) {
918            return {
919                ErrorCode    => 'TicketCreate.InvalidParameter',
920                ErrorMessage => "TicketCreate: Article->Charset is invalid!",
921            };
922        }
923    }
924
925    # check Article->ContentType
926    if ( $Article->{ContentType} ) {
927
928        $Article->{ContentType} = lc $Article->{ContentType};
929
930        # check Charset part
931        my $Charset = '';
932        if ( $Article->{ContentType} =~ /charset=/i ) {
933            $Charset = $Article->{ContentType};
934            $Charset =~ s/.+?charset=("|'|)(\w+)/$2/gi;
935            $Charset =~ s/"|'//g;
936            $Charset =~ s/(.+?);.*/$1/g;
937        }
938
939        if ( !$Self->ValidateCharset( Charset => $Charset ) ) {
940            return {
941                ErrorCode    => 'TicketCreate.InvalidParameter',
942                ErrorMessage => "TicketCreate: Article->ContentType is invalid!",
943            };
944        }
945
946        # check MimeType part
947        my $MimeType = '';
948        if ( $Article->{ContentType} =~ /^(\w+\/\w+)/i ) {
949            $MimeType = $1;
950            $MimeType =~ s/"|'//g;
951        }
952
953        if ( !$Self->ValidateMimeType( MimeType => $MimeType ) ) {
954            return {
955                ErrorCode    => 'TicketCreate.InvalidParameter',
956                ErrorMessage => "TicketCreate: Article->ContentType is invalid!",
957            };
958        }
959    }
960
961    # check Article->HistoryType
962    if ( !$Article->{HistoryType} ) {
963
964        # return internal server error
965        return {
966            ErrorMessage => "TicketCreate: Article-> HistoryType is required and Sysconfig"
967                . " HistoryType setting could not be read!"
968        };
969    }
970    if ( !$Self->ValidateHistoryType( %{$Article} ) ) {
971        return {
972            ErrorCode    => 'TicketCreate.InvalidParameter',
973            ErrorMessage => "TicketCreate: Article->HistoryType parameter is invalid!",
974        };
975    }
976
977    # check Article->HistoryComment
978    if ( !$Article->{HistoryComment} ) {
979
980        # return internal server error
981        return {
982            ErrorMessage => "TicketCreate: Article->HistoryComment is required and Sysconfig"
983                . " HistoryComment setting could not be read!"
984        };
985    }
986
987    # get config object
988    my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
989
990    # check Article->TimeUnit
991    # TimeUnit could be required or not depending on sysconfig option
992    if (
993        ( !defined $Article->{TimeUnit} || !IsStringWithData( $Article->{TimeUnit} ) )
994        && $ConfigObject->{'Ticket::Frontend::AccountTime'}
995        && $ConfigObject->{'Ticket::Frontend::NeedAccountedTime'}
996        )
997    {
998        return {
999            ErrorCode    => 'TicketCreate.MissingParameter',
1000            ErrorMessage => "TicketCreate: Article->TimeUnit is required by sysconfig option!",
1001        };
1002    }
1003    if ( $Article->{TimeUnit} ) {
1004        if ( !$Self->ValidateTimeUnit( %{$Article} ) ) {
1005            return {
1006                ErrorCode    => 'TicketCreate.InvalidParameter',
1007                ErrorMessage => "TicketCreate: Article->TimeUnit parameter is invalid!",
1008            };
1009        }
1010    }
1011
1012    # check Article->NoAgentNotify
1013    if ( $Article->{NoAgentNotify} && $Article->{NoAgentNotify} ne '1' ) {
1014        return {
1015            ErrorCode    => 'TicketCreate.InvalidParameter',
1016            ErrorMessage => "TicketCreate: Article->NoAgent parameter is invalid!",
1017        };
1018    }
1019
1020    # check Article array parameters
1021    for my $Attribute (
1022        qw( ForceNotificationToUserID ExcludeNotificationToUserID ExcludeMuteNotificationToUserID )
1023        )
1024    {
1025        if ( defined $Article->{$Attribute} ) {
1026
1027            # check structure
1028            if ( IsHashRefWithData( $Article->{$Attribute} ) ) {
1029                return {
1030                    ErrorCode    => 'TicketCreate.InvalidParameter',
1031                    ErrorMessage => "TicketCreate: Article->$Attribute parameter is invalid!",
1032                };
1033            }
1034            else {
1035                if ( !IsArrayRefWithData( $Article->{$Attribute} ) ) {
1036                    $Article->{$Attribute} = [ $Article->{$Attribute} ];
1037                }
1038                for my $UserID ( @{ $Article->{$Attribute} } ) {
1039                    if ( !$Self->ValidateUserID( UserID => $UserID ) ) {
1040                        return {
1041                            ErrorCode    => 'TicketCreate.InvalidParameter',
1042                            ErrorMessage => "TicketCreate: Article->$Attribute UserID=$UserID"
1043                                . " parameter is invalid!",
1044                        };
1045                    }
1046                }
1047            }
1048        }
1049    }
1050
1051    # if everything is OK then return Success
1052    return {
1053        Success => 1,
1054    };
1055}
1056
1057=head2 _CheckDynamicField()
1058
1059checks if the given dynamic field parameter is valid.
1060
1061    my $DynamicFieldCheck = $OperationObject->_CheckDynamicField(
1062        DynamicField => $DynamicField,              # all dynamic field parameters
1063    );
1064
1065    returns:
1066
1067    $DynamicFieldCheck = {
1068        Success => 1,                               # if everething is OK
1069    }
1070
1071    $DynamicFieldCheck = {
1072        ErrorCode    => 'Function.Error',           # if error
1073        ErrorMessage => 'Error description',
1074    }
1075
1076=cut
1077
1078sub _CheckDynamicField {
1079    my ( $Self, %Param ) = @_;
1080
1081    my $DynamicField = $Param{DynamicField};
1082
1083    # check DynamicField item internally
1084    for my $Needed (qw(Name Value)) {
1085        if (
1086            !defined $DynamicField->{$Needed}
1087            || ( !IsString( $DynamicField->{$Needed} ) && ref $DynamicField->{$Needed} ne 'ARRAY' )
1088            )
1089        {
1090            return {
1091                ErrorCode    => 'TicketCreate.MissingParameter',
1092                ErrorMessage => "TicketCreate: DynamicField->$Needed  parameter is missing!",
1093            };
1094        }
1095    }
1096
1097    # check DynamicField->Name
1098    if ( !$Self->ValidateDynamicFieldName( %{$DynamicField} ) ) {
1099        return {
1100            ErrorCode    => 'TicketCreate.InvalidParameter',
1101            ErrorMessage => "TicketCreate: DynamicField->Name parameter is invalid!",
1102        };
1103    }
1104
1105    # check DynamicField->Value
1106    if ( !$Self->ValidateDynamicFieldValue( %{$DynamicField} ) ) {
1107        return {
1108            ErrorCode    => 'TicketCreate.InvalidParameter',
1109            ErrorMessage => "TicketCreate: DynamicField->Value parameter is invalid!",
1110        };
1111    }
1112
1113    # if everything is OK then return Success
1114    return {
1115        Success => 1,
1116    };
1117}
1118
1119=head2 _CheckAttachment()
1120
1121checks if the given attachment parameter is valid.
1122
1123    my $AttachmentCheck = $OperationObject->_CheckAttachment(
1124        Attachment => $Attachment,                  # all attachment parameters
1125    );
1126
1127    returns:
1128
1129    $AttachmentCheck = {
1130        Success => 1,                               # if everething is OK
1131    }
1132
1133    $AttachmentCheck = {
1134        ErrorCode    => 'Function.Error',           # if error
1135        ErrorMessage => 'Error description',
1136    }
1137
1138=cut
1139
1140sub _CheckAttachment {
1141    my ( $Self, %Param ) = @_;
1142
1143    my $Attachment = $Param{Attachment};
1144
1145    # check attachment item internally
1146    for my $Needed (qw(Content ContentType Filename)) {
1147        if ( !IsStringWithData( $Attachment->{$Needed} ) ) {
1148            return {
1149                ErrorCode    => 'TicketCreate.MissingParameter',
1150                ErrorMessage => "TicketCreate: Attachment->$Needed  parameter is missing!",
1151            };
1152        }
1153    }
1154
1155    # check Article->ContentType
1156    if ( $Attachment->{ContentType} ) {
1157
1158        $Attachment->{ContentType} = lc $Attachment->{ContentType};
1159
1160        # check Charset part
1161        my $Charset = '';
1162        if ( $Attachment->{ContentType} =~ /charset=/i ) {
1163            $Charset = $Attachment->{ContentType};
1164            $Charset =~ s/.+?charset=("|'|)(\w+)/$2/gi;
1165            $Charset =~ s/"|'//g;
1166            $Charset =~ s/(.+?);.*/$1/g;
1167        }
1168
1169        if ( $Charset && !$Self->ValidateCharset( Charset => $Charset ) ) {
1170            return {
1171                ErrorCode    => 'TicketCreate.InvalidParameter',
1172                ErrorMessage => "TicketCreate: Attachment->ContentType is invalid!",
1173            };
1174        }
1175
1176        # check MimeType part
1177        my $MimeType = '';
1178        if ( $Attachment->{ContentType} =~ /^(\w+\/\w+)/i ) {
1179            $MimeType = $1;
1180            $MimeType =~ s/"|'//g;
1181        }
1182
1183        if ( !$Self->ValidateMimeType( MimeType => $MimeType ) ) {
1184            return {
1185                ErrorCode    => 'TicketCreate.InvalidParameter',
1186                ErrorMessage => "TicketCreate: Attachment->ContentType is invalid!",
1187            };
1188        }
1189    }
1190
1191    # if everything is OK then return Success
1192    return {
1193        Success => 1,
1194    };
1195}
1196
1197=head2 _TicketCreate()
1198
1199creates a ticket with its article and sets dynamic fields and attachments if specified.
1200
1201    my $Response = $OperationObject->_TicketCreate(
1202        Ticket       => $Ticket,                  # all ticket parameters
1203        Article      => $Article,                 # all attachment parameters
1204        DynamicField => $DynamicField,            # all dynamic field parameters
1205        Attachment   => $Attachment,             # all attachment parameters
1206        UserID       => 123,
1207    );
1208
1209    returns:
1210
1211    $Response = {
1212        Success => 1,                               # if everything was OK
1213        Data => {
1214            TicketID     => 123,
1215            TicketNumber => 'TN3422332',
1216            ArticleID    => 123,
1217        }
1218    }
1219
1220    $Response = {
1221        Success      => 0,                         # if unexpected error
1222        ErrorMessage => "$Param{ErrorCode}: $Param{ErrorMessage}",
1223    }
1224
1225=cut
1226
1227sub _TicketCreate {
1228    my ( $Self, %Param ) = @_;
1229
1230    my $Ticket           = $Param{Ticket};
1231    my $Article          = $Param{Article};
1232    my $DynamicFieldList = $Param{DynamicFieldList};
1233    my $AttachmentList   = $Param{AttachmentList};
1234    my $CustomerUser     = $Ticket->{CustomerUser} || '';
1235
1236    # Get customer information, that will be used to create the ticket.
1237    # If TicketCreate CustomerUser parameter is defined,
1238    #   check if there is CustomerUser in DB with such address,
1239    # If address, defined in $Ticket->{CustomerUser},
1240    #    is not valid, ValidateCustomer() will not allowed to run TicketCreate.
1241    # See more information in bug#14288.
1242    my $CustomerUserObject = $Kernel::OM->Get('Kernel::System::CustomerUser');
1243    my %CustomerUserData   = $CustomerUserObject->CustomerUserDataGet(
1244        User => $Ticket->{CustomerUser},
1245    );
1246
1247    if ( !IsHashRefWithData( \%CustomerUserData ) ) {
1248        my %CustomerSearch = $CustomerUserObject->CustomerSearch(
1249            PostMasterSearch => $CustomerUser,
1250            Limit            => 1,
1251        );
1252
1253        if ( IsHashRefWithData( \%CustomerSearch ) ) {
1254            my @CustomerSearchResults = sort keys %CustomerSearch;
1255            $CustomerUser = $CustomerSearchResults[0];
1256
1257            %CustomerUserData = $CustomerUserObject->CustomerUserDataGet(
1258                User => $CustomerUser,
1259            );
1260        }
1261
1262    }
1263    else {
1264        $CustomerUser = $CustomerUserData{UserLogin};
1265    }
1266
1267    my $CustomerID = $CustomerUserData{UserCustomerID} || '';
1268
1269    # use user defined CustomerID if defined
1270    if ( defined $Ticket->{CustomerID} && $Ticket->{CustomerID} ne '' ) {
1271        $CustomerID = $Ticket->{CustomerID};
1272    }
1273
1274    # get database object
1275    my $UserObject = $Kernel::OM->Get('Kernel::System::User');
1276
1277    my $OwnerID;
1278    if ( $Ticket->{Owner} && !$Ticket->{OwnerID} ) {
1279        my %OwnerData = $UserObject->GetUserData(
1280            User => $Ticket->{Owner},
1281        );
1282        $OwnerID = $OwnerData{UserID};
1283    }
1284    elsif ( defined $Ticket->{OwnerID} ) {
1285        $OwnerID = $Ticket->{OwnerID};
1286    }
1287
1288    my $ResponsibleID;
1289    if ( $Ticket->{Responsible} && !$Ticket->{ResponsibleID} ) {
1290        my %ResponsibleData = $UserObject->GetUserData(
1291            User => $Ticket->{Responsible},
1292        );
1293        $ResponsibleID = $ResponsibleData{UserID};
1294    }
1295    elsif ( defined $Ticket->{ResponsibleID} ) {
1296        $ResponsibleID = $Ticket->{ResponsibleID};
1297    }
1298
1299    # get ticket object
1300    my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');
1301
1302    # create new ticket
1303    my $TicketID = $TicketObject->TicketCreate(
1304        Title        => $Ticket->{Title},
1305        QueueID      => $Ticket->{QueueID} || '',
1306        Queue        => $Ticket->{Queue} || '',
1307        Lock         => 'unlock',
1308        TypeID       => $Ticket->{TypeID} || '',
1309        Type         => $Ticket->{Type} || '',
1310        ServiceID    => $Ticket->{ServiceID} || '',
1311        Service      => $Ticket->{Service} || '',
1312        SLAID        => $Ticket->{SLAID} || '',
1313        SLA          => $Ticket->{SLA} || '',
1314        StateID      => $Ticket->{StateID} || '',
1315        State        => $Ticket->{State} || '',
1316        PriorityID   => $Ticket->{PriorityID} || '',
1317        Priority     => $Ticket->{Priority} || '',
1318        OwnerID      => 1,
1319        CustomerNo   => $CustomerID,
1320        CustomerUser => $CustomerUser || '',
1321        UserID       => $Param{UserID},
1322    );
1323
1324    if ( !$TicketID ) {
1325        return {
1326            Success      => 0,
1327            ErrorMessage => 'Ticket could not be created, please contact the system administrator',
1328        };
1329    }
1330
1331    # set lock if specified
1332    if ( $Ticket->{Lock} || $Ticket->{LockID} ) {
1333        $TicketObject->TicketLockSet(
1334            TicketID => $TicketID,
1335            LockID   => $Ticket->{LockID} || '',
1336            Lock     => $Ticket->{Lock} || '',
1337            UserID   => $Param{UserID},
1338        );
1339    }
1340
1341    # get State Data
1342    my %StateData;
1343    my $StateID;
1344
1345    # get state object
1346    my $StateObject = $Kernel::OM->Get('Kernel::System::State');
1347
1348    if ( $Ticket->{StateID} ) {
1349        $StateID = $Ticket->{StateID};
1350    }
1351    else {
1352        $StateID = $StateObject->StateLookup(
1353            State => $Ticket->{State},
1354        );
1355    }
1356
1357    %StateData = $StateObject->StateGet(
1358        ID => $StateID,
1359    );
1360
1361    # force unlock if state type is close
1362    if ( $StateData{TypeName} =~ /^close/i ) {
1363
1364        # set lock
1365        $TicketObject->TicketLockSet(
1366            TicketID => $TicketID,
1367            Lock     => 'unlock',
1368            UserID   => $Param{UserID},
1369        );
1370    }
1371
1372    # set pending time
1373    elsif ( $StateData{TypeName} =~ /^pending/i ) {
1374
1375        # set pending time
1376        if ( defined $Ticket->{PendingTime} ) {
1377            $TicketObject->TicketPendingTimeSet(
1378                UserID   => $Param{UserID},
1379                TicketID => $TicketID,
1380                %{ $Ticket->{PendingTime} },
1381            );
1382        }
1383    }
1384
1385    # set dynamic fields (only for object type 'ticket')
1386    if ( IsArrayRefWithData($DynamicFieldList) ) {
1387
1388        DYNAMICFIELD:
1389        for my $DynamicField ( @{$DynamicFieldList} ) {
1390            next DYNAMICFIELD if !$Self->ValidateDynamicFieldObjectType( %{$DynamicField} );
1391
1392            my $Result = $Self->SetDynamicFieldValue(
1393                %{$DynamicField},
1394                TicketID => $TicketID,
1395                UserID   => $Param{UserID},
1396            );
1397
1398            if ( !$Result->{Success} ) {
1399                my $ErrorMessage =
1400                    $Result->{ErrorMessage} || "Dynamic Field $DynamicField->{Name} could not be"
1401                    . " set, please contact the system administrator";
1402
1403                return {
1404                    Success      => 0,
1405                    ErrorMessage => $ErrorMessage,
1406                };
1407            }
1408        }
1409    }
1410
1411    if ( !defined $Article->{NoAgentNotify} ) {
1412
1413        # check if new owner is given (then send no agent notify)
1414        $Article->{NoAgentNotify} = 0;
1415        if ($OwnerID) {
1416            $Article->{NoAgentNotify} = 1;
1417        }
1418    }
1419
1420    # set Article From
1421    my $From;
1422    if ( $Article->{From} ) {
1423        $From = $Article->{From};
1424    }
1425
1426    # use data from customer user (if customer user is in database)
1427    elsif ( IsHashRefWithData( \%CustomerUserData ) ) {
1428        $From = '"' . $CustomerUserData{UserFullname} . '"'
1429            . ' <' . $CustomerUserData{UserEmail} . '>';
1430    }
1431
1432    # otherwise use customer user as sent from the request (it should be an email)
1433    else {
1434        $From = $CustomerUser;
1435    }
1436
1437    # set Article To
1438    my $To;
1439    if ( $Ticket->{Queue} ) {
1440        $To = $Ticket->{Queue};
1441    }
1442    else {
1443        $To = $Kernel::OM->Get('Kernel::System::Queue')->QueueLookup(
1444            QueueID => $Ticket->{QueueID},
1445        );
1446    }
1447
1448    if ( !$Article->{CommunicationChannel} ) {
1449
1450        my %CommunicationChannel = $Kernel::OM->Get('Kernel::System::CommunicationChannel')->ChannelGet(
1451            ChannelID => $Article->{CommunicationChannelID},
1452        );
1453        $Article->{CommunicationChannel} = $CommunicationChannel{ChannelName};
1454    }
1455
1456    my $ArticleBackendObject = $Kernel::OM->Get('Kernel::System::Ticket::Article')->BackendForChannel(
1457        ChannelName => $Article->{CommunicationChannel},
1458    );
1459
1460    my $PlainBody = $Article->{Body};
1461
1462    # Convert article body to plain text, if HTML content was supplied. This is necessary since auto response code
1463    #   expects plain text content. Please see bug#13397 for more information.
1464    if ( $Article->{ContentType} =~ /text\/html/i || $Article->{MimeType} =~ /text\/html/i ) {
1465        $PlainBody = $Kernel::OM->Get('Kernel::System::HTMLUtils')->ToAscii(
1466            String => $Article->{Body},
1467        );
1468    }
1469
1470    # Create article.
1471    my $ArticleID = $ArticleBackendObject->ArticleCreate(
1472        NoAgentNotify        => $Article->{NoAgentNotify} || 0,
1473        TicketID             => $TicketID,
1474        SenderTypeID         => $Article->{SenderTypeID} || '',
1475        SenderType           => $Article->{SenderType} || '',
1476        IsVisibleForCustomer => $Article->{IsVisibleForCustomer},
1477        From                 => $From,
1478        To                   => $To,
1479        Subject              => $Article->{Subject},
1480        Body                 => $Article->{Body},
1481        MimeType             => $Article->{MimeType} || '',
1482        Charset              => $Article->{Charset} || '',
1483        ContentType          => $Article->{ContentType} || '',
1484        UserID               => $Param{UserID},
1485        HistoryType          => $Article->{HistoryType},
1486        HistoryComment       => $Article->{HistoryComment} || '%%',
1487        AutoResponseType     => $Article->{AutoResponseType},
1488        OrigHeader           => {
1489            From    => $From,
1490            To      => $To,
1491            Subject => $Article->{Subject},
1492            Body    => $PlainBody,
1493        },
1494    );
1495
1496    if ( !$ArticleID ) {
1497        return {
1498            Success      => 0,
1499            ErrorMessage => 'Article could not be created, please contact the system administrator',
1500        };
1501    }
1502
1503    # set owner (if owner or owner id is given)
1504    if ($OwnerID) {
1505        $TicketObject->TicketOwnerSet(
1506            TicketID  => $TicketID,
1507            NewUserID => $OwnerID,
1508            UserID    => $Param{UserID},
1509        );
1510
1511        # set lock if no lock was defined
1512        if ( !$Ticket->{Lock} && !$Ticket->{LockID} ) {
1513            $TicketObject->TicketLockSet(
1514                TicketID => $TicketID,
1515                Lock     => 'lock',
1516                UserID   => $Param{UserID},
1517            );
1518        }
1519    }
1520
1521    # else set owner to current agent but do not lock it
1522    else {
1523        $TicketObject->TicketOwnerSet(
1524            TicketID           => $TicketID,
1525            NewUserID          => $Param{UserID},
1526            SendNoNotification => 1,
1527            UserID             => $Param{UserID},
1528        );
1529    }
1530
1531    # set responsible
1532    if ($ResponsibleID) {
1533        $TicketObject->TicketResponsibleSet(
1534            TicketID  => $TicketID,
1535            NewUserID => $ResponsibleID,
1536            UserID    => $Param{UserID},
1537        );
1538    }
1539
1540    # time accounting
1541    if ( $Article->{TimeUnit} ) {
1542        $TicketObject->TicketAccountTime(
1543            TicketID  => $TicketID,
1544            ArticleID => $ArticleID,
1545            TimeUnit  => $Article->{TimeUnit},
1546            UserID    => $Param{UserID},
1547        );
1548    }
1549
1550    # set dynamic fields (only for object type 'article')
1551    if ( IsArrayRefWithData($DynamicFieldList) ) {
1552
1553        DYNAMICFIELD:
1554        for my $DynamicField ( @{$DynamicFieldList} ) {
1555
1556            my $IsArticleDynamicField = $Self->ValidateDynamicFieldObjectType(
1557                %{$DynamicField},
1558                Article => 1,
1559            );
1560            next DYNAMICFIELD if !$IsArticleDynamicField;
1561
1562            my $Result = $Self->SetDynamicFieldValue(
1563                %{$DynamicField},
1564                TicketID  => $TicketID,
1565                ArticleID => $ArticleID,
1566                UserID    => $Param{UserID},
1567            );
1568
1569            if ( !$Result->{Success} ) {
1570                my $ErrorMessage =
1571                    $Result->{ErrorMessage} || "Dynamic Field $DynamicField->{Name} could not be"
1572                    . " set, please contact the system administrator";
1573
1574                return {
1575                    Success      => 0,
1576                    ErrorMessage => $ErrorMessage,
1577                };
1578            }
1579        }
1580    }
1581
1582    # set attachments
1583    if ( IsArrayRefWithData($AttachmentList) ) {
1584
1585        for my $Attachment ( @{$AttachmentList} ) {
1586            my $Result = $Self->CreateAttachment(
1587                TicketID   => $TicketID,
1588                Attachment => $Attachment,
1589                ArticleID  => $ArticleID,
1590                UserID     => $Param{UserID}
1591            );
1592
1593            if ( !$Result->{Success} ) {
1594                my $ErrorMessage =
1595                    $Result->{ErrorMessage} || "Attachment could not be created, please contact"
1596                    . " the system administrator";
1597
1598                return {
1599                    Success      => 0,
1600                    ErrorMessage => $ErrorMessage,
1601                };
1602            }
1603        }
1604    }
1605
1606    # get ticket data
1607    my %TicketData = $TicketObject->TicketGet(
1608        TicketID      => $TicketID,
1609        DynamicFields => 1,
1610        UserID        => $Param{UserID},
1611    );
1612
1613    if ( !IsHashRefWithData( \%TicketData ) ) {
1614        return {
1615            Success      => 0,
1616            ErrorMessage => 'Could not get new ticket information, please contact the system'
1617                . ' administrator',
1618        };
1619    }
1620
1621    # get web service configuration
1622    my $Webservice = $Kernel::OM->Get('Kernel::System::GenericInterface::Webservice')->WebserviceGet(
1623        ID => $Self->{WebserviceID},
1624    );
1625
1626    my $IncludeTicketData;
1627
1628    # Get operation config, if operation name was supplied.
1629    if ( $Self->{Operation} ) {
1630        my $OperationConfig = $Webservice->{Config}->{Provider}->{Operation}->{ $Self->{Operation} };
1631        $IncludeTicketData = $OperationConfig->{IncludeTicketData};
1632    }
1633
1634    if ( !$IncludeTicketData ) {
1635        return {
1636            Success => 1,
1637            Data    => {
1638                TicketID     => $TicketID,
1639                TicketNumber => $TicketData{TicketNumber},
1640                ArticleID    => $ArticleID,
1641            },
1642        };
1643    }
1644
1645    # extract all dynamic fields from main ticket hash.
1646    my %TicketDynamicFields;
1647    TICKETATTRIBUTE:
1648    for my $TicketAttribute ( sort keys %TicketData ) {
1649        if ( $TicketAttribute =~ m{\A DynamicField_(.*) \z}msx ) {
1650            $TicketDynamicFields{$1} = {
1651                Name  => $1,
1652                Value => $TicketData{$TicketAttribute},
1653            };
1654            delete $TicketData{$TicketAttribute};
1655        }
1656    }
1657
1658    # add dynamic fields as array into 'DynamicField' hash key if any
1659    if (%TicketDynamicFields) {
1660        $TicketData{DynamicField} = [ sort { $a->{Name} cmp $b->{Name} } values %TicketDynamicFields ];
1661    }
1662
1663    # prepare TicketData and get Article
1664    my %ArticleData = $ArticleBackendObject->ArticleGet(
1665        TicketID      => $TicketID,
1666        ArticleID     => $ArticleID,
1667        DynamicFields => 1,
1668    );
1669
1670    # prepare Article DynamicFields
1671    my @ArticleDynamicFields;
1672
1673    # remove all dynamic fields form main ticket hash and set them into an array.
1674    ARTICLEATTRIBUTE:
1675    for my $ArticleAttribute ( sort keys %ArticleData ) {
1676        if ( $ArticleAttribute =~ m{\A DynamicField_(.*) \z}msx ) {
1677            if ( !exists $TicketDynamicFields{$1} ) {
1678                push @ArticleDynamicFields, {
1679                    Name  => $1,
1680                    Value => $ArticleData{$ArticleAttribute},
1681                };
1682            }
1683
1684            delete $ArticleData{$ArticleAttribute};
1685        }
1686    }
1687
1688    # add dynamic fields array into 'DynamicField' hash key if any
1689    if (@ArticleDynamicFields) {
1690        $ArticleData{DynamicField} = \@ArticleDynamicFields;
1691    }
1692
1693    # add attachment if the request includes attachments
1694    if ( IsArrayRefWithData($AttachmentList) ) {
1695        my %AttachmentIndex = $ArticleBackendObject->ArticleAttachmentIndex(
1696            ArticleID => $ArticleID,
1697        );
1698
1699        my @Attachments;
1700        $Kernel::OM->Get('Kernel::System::Main')->Require('MIME::Base64');
1701        ATTACHMENT:
1702        for my $FileID ( sort keys %AttachmentIndex ) {
1703            next ATTACHMENT if !$FileID;
1704            my %Attachment = $ArticleBackendObject->ArticleAttachment(
1705                ArticleID => $ArticleID,
1706                FileID    => $FileID,
1707            );
1708
1709            next ATTACHMENT if !IsHashRefWithData( \%Attachment );
1710
1711            # convert content to base64, but prevent 76 chars brake, see bug#14500.
1712            $Attachment{Content} = MIME::Base64::encode_base64( $Attachment{Content}, '' );
1713            push @Attachments, {%Attachment};
1714        }
1715
1716        # set Attachments data
1717        if (@Attachments) {
1718            $ArticleData{Attachment} = \@Attachments;
1719        }
1720    }
1721
1722    $TicketData{Article} = \%ArticleData;
1723
1724    return {
1725        Success => 1,
1726        Data    => {
1727            TicketID     => $TicketID,
1728            TicketNumber => $TicketData{TicketNumber},
1729            ArticleID    => $ArticleID,
1730            Ticket       => \%TicketData,
1731        },
1732    };
1733}
1734
17351;
1736
1737=end Internal:
1738
1739=head1 TERMS AND CONDITIONS
1740
1741This software is part of the OTRS project (L<https://otrs.org/>).
1742
1743This software comes with ABSOLUTELY NO WARRANTY. For details, see
1744the enclosed file COPYING for license information (GPL). If you
1745did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
1746
1747=cut
1748