# --
# Copyright (C) 2001-2021 OTRS AG, https://otrs.com/
# Copyright (C) 2021 Znuny GmbH, https://znuny.org/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --

package Kernel::System::ProcessManagement::Process;

use strict;
use warnings;
use utf8;

use Kernel::System::VariableCheck qw(:all);

our @ObjectDependencies = (
    'Kernel::Config',
    'Kernel::System::DynamicField',
    'Kernel::System::DynamicField::Backend',
    'Kernel::System::Log',
    'Kernel::System::ProcessManagement::Activity',
    'Kernel::System::ProcessManagement::ActivityDialog',
    'Kernel::System::ProcessManagement::DB::Process',
    'Kernel::System::ProcessManagement::Transition',
    'Kernel::System::ProcessManagement::TransitionAction',
    'Kernel::System::Ticket',
);

=head1 NAME

Kernel::System::ProcessManagement::Process - process lib

=head1 DESCRIPTION

All ProcessManagement Process functions.

=head1 PUBLIC INTERFACE

=head2 new()

Don't use the constructor directly, use the ObjectManager instead:

    my $ProcessObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::Process');

=cut

sub new {
    my ( $Type, %Param ) = @_;

    # allocate new hash for object
    my $Self = {};
    bless( $Self, $Type );

    # 0=off; 1=on;
    $Self->{Debug} = $Param{Debug} || 0;

    return $Self;
}

=head2 ProcessGet()

    Get process info

    my $Process = $ProcessObject->ProcessGet(
        ProcessEntityID => 'P1',
        Preferences     => 1,    # (optional) default 1 (0|1), if set to 1, returns Preferences
    );

    Returns:

    $Process = {
        'Name' => 'Process1',
        'CreateBy'            => '1',
        'CreateTime'          => '16-02-2012 13:37:00',
        'ChangeBy'            => '1',
        'ChangeTime'          => '17-02-2012 13:37:00',
        'State'               => 'Active',
        'StartActivityDialog' => 'AD1',
        'StartActivity'       => 'A1',
        'Path' => {
            'A2' => {
                'T3' => {
                    ActivityEntityID => 'A4',
                },
            },
            'A1' => {
                'T1' => {
                    ActivityEntityID => 'A2',
                },
                'T2' => {
                    ActivityEntityID => 'A3',
                },
            },
        },
    };

=cut

sub ProcessGet {
    my ( $Self, %Param ) = @_;

    for my $Needed (qw(ProcessEntityID)) {
        if ( !defined $Param{$Needed} ) {
            $Kernel::OM->Get('Kernel::System::Log')->Log(
                Priority => 'error',
                Message  => "Need $Needed!",
            );
            return;
        }
    }

    $Param{Preferences} = $Param{Preferences} || 1;

    my $Process = $Kernel::OM->Get('Kernel::Config')->Get('Process');

    if ( !IsHashRefWithData($Process) ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => 'Need Process config!',
        );
        return;
    }

    if ( !IsHashRefWithData( $Process->{ $Param{ProcessEntityID} } ) ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "No Data for Process '$Param{ProcessEntityID}' found!",
        );
        return;
    }

    # get process preferences

    if ( $Param{Preferences} ) {
        my %Preferences = $Self->ProcessPreferencesGet(
            ProcessEntityID => $Param{ProcessEntityID},
            Export          => $Param{Export},
        );

        # merge data
        if (%Preferences) {
            %{ $Process->{ $Param{ProcessEntityID} } } = ( %{ $Process->{ $Param{ProcessEntityID} } }, %Preferences );
        }
    }

    return $Process->{ $Param{ProcessEntityID} };
}

=head2 ProcessList()

    Get a list of all Processes

    my $ProcessList = $ProcessObject->ProcessList(
        ProcessState => ['Active'],           # Active, FadeAway, Inactive
        Interface    => ['AgentInterface'],   # optional, ['AgentInterface'] or ['CustomerInterface'] or ['AgentInterface', 'CustomerInterface'] or 'all'
        Silent       => 1                     # optional, 1 || 0, default 0, if set to 1 does not log errors if there are no processes configured
    );

    Returns:

    $ProcessList = {
        'P1' => 'Process 1',
        'P2' => 'Process 2',
        'P3' => '',
    };

=cut

sub ProcessList {
    my ( $Self, %Param ) = @_;

    for my $Needed (qw(ProcessState)) {
        if ( !defined $Param{$Needed} ) {
            $Kernel::OM->Get('Kernel::System::Log')->Log(
                Priority => 'error',
                Message  => "Need $Needed!",
            );
            return;
        }
    }

    if ( !IsArrayRefWithData( $Param{ProcessState} ) ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => 'Need at least one ProcessState!',
        );
        return;
    }

    my $Processes = $Kernel::OM->Get('Kernel::Config')->Get('Process');
    if ( !IsHashRefWithData($Processes) ) {

        if ( !$Param{Silent} ) {
            $Kernel::OM->Get('Kernel::System::Log')->Log(
                Priority => 'error',
                Message  => 'Need Process config!',
            );
        }

        return;
    }

    # get only processes with the requested ProcessState(s)
    my %ProcessList;
    for my $ProcessEntityID ( sort keys %{$Processes} ) {
        if ( grep { $_ eq $Processes->{$ProcessEntityID}->{State} } @{ $Param{ProcessState} } ) {
            $ProcessList{$ProcessEntityID} = $Processes->{$ProcessEntityID}->{Name} || '';
        }
    }

    # set Interface parameter to 'all' by default if parameter was not set
    if ( !defined $Param{Interface} ) {
        $Param{Interface} = 'all';
    }

    # if Interface is 'all' return all processes without interface restrictions
    if ( $Param{Interface} eq 'all' ) {
        return \%ProcessList;
    }

    # get activity dialog object
    my $ActivityDialogObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::ActivityDialog');

    # otherwise return only processes where the initial activity dialog matches given interface
    my %ReducedProcessList;
    PROCESS:
    for my $ProcessEntityID ( sort keys %ProcessList ) {

        # get process start point for each process we already got
        my $Start = $Self->ProcessStartpointGet( ProcessEntityID => $ProcessEntityID );

        # skip processes if they does not have a valid start point
        next PROCESS if !IsHashRefWithData($Start);
        next PROCESS if !IsStringWithData( $Start->{ActivityDialog} );

        # try to get the start ActivityDialog for the given interface
        my $ActivityDialog = $ActivityDialogObject->ActivityDialogGet(
            ActivityDialogEntityID => $Start->{ActivityDialog},
            Interface              => $Param{Interface},
            Silent                 => 1,
        );

        # skip process if first activity dialog could not be got for the given interface
        next PROCESS if !IsHashRefWithData($ActivityDialog);

        $ReducedProcessList{$ProcessEntityID} = $ProcessList{$ProcessEntityID};
    }

    return \%ReducedProcessList;
}

=head2 ProcessStartpointGet()

    Get process startpoint

    my $Start = $ProcessObject->ProcessStartpointGet(
        ProcessEntityID => 'P1',
    );

    Returns:

    $Start = {
        Activity        => 'A1',
        ActivityDialog  => 'AD1',
    };

=cut

sub ProcessStartpointGet {
    my ( $Self, %Param ) = @_;

    for my $Needed (qw(ProcessEntityID)) {
        if ( !defined $Param{$Needed} ) {
            $Kernel::OM->Get('Kernel::System::Log')->Log(
                Priority => 'error',
                Message  => "Need $Needed!",
            );
            return;
        }
    }

    my $Process = $Self->ProcessGet(
        ProcessEntityID => $Param{ProcessEntityID},
        Preferences     => 0,
    );

    # include FadeAway processes so they will be listed as available processes to continue working
    #    with them
    if ( $Process->{State} ne 'Active' && $Process->{State} ne 'FadeAway' ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "Can't get 'StartActivity' for Process '$Param{ProcessEntityID}', State"
                . " is '$Process->{State}'!",
        );
        return;
    }

    if ( !$Process->{StartActivity} ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "No 'StartActivity' for Process '$Param{ProcessEntityID}' found!",
        );
        return;
    }

    if ( !$Process->{StartActivity} ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "No 'StartActivity' for Process '$Param{ProcessEntityID}' found!",
        );
        return;
    }

    if ( !$Process->{StartActivityDialog} ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "No 'StartActivityDialog' for Process '$Param{ProcessEntityID}' found!",
        );
        return;
    }
    return {
        Activity       => $Process->{StartActivity},
        ActivityDialog => $Process->{StartActivityDialog}
    };
}

=head2 ProcessTransition()

    Check valid Transitions and Change Ticket's Activity
    if a Transition was positively checked

    my $ProcessTransition = $ProcessObject->ProcessTransition(
        ProcessEntityID  => 'P1',
        ActivityEntityID => 'A1',
        TicketID         => 123,
        UserID           => 123,
        CheckOnly        => 1,             # optional
        Data             => {              # optional
            Queue         => 'Raw',
            DynamicField1 => 'Value',
            Subject       => 'Testsubject',
            #...
        },
    );

Returns:

    $Success = 1; # undef # if "CheckOnly" is NOT set
    1 if Transition was executed and Ticket->ActivityEntityID updated
    undef if no Transition matched or check failed otherwise

    $ProcessTransition = {  # if option "CheckOnly" is set
        'T1' => {
            ActivityEntityID => 'A1',
            TransitionAction => [
                'TA1',
                'TA2',
                'TA3',
            ],
        },
    };

=cut

sub ProcessTransition {
    my ( $Self, %Param ) = @_;

    for my $Needed (qw(ProcessEntityID ActivityEntityID TicketID UserID)) {
        if ( !defined $Param{$Needed} ) {
            $Kernel::OM->Get('Kernel::System::Log')->Log(
                Priority => 'error',
                Message  => "Need $Needed!",
            );
            return;
        }
    }

    my %Data;

    # Get Ticket object
    my $TicketObject = $Kernel::OM->Get('Kernel::System::Ticket');

    # Get Ticket Data
    %Data = $TicketObject->TicketGet(
        TicketID      => $Param{TicketID},
        DynamicFields => 1,
        UserID        => $Param{UserID},
    );

    # Check if we got a Ticket
    if ( !IsHashRefWithData( \%Data ) ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "Invalid TicketID: $Param{TicketID}!",
        );
        return;
    }

    # If we have Data lay it over the current %Data
    # to check Ticket + Additional Data
    if ( $Param{Data} ) {
        %Data = ( %Data, %{ $Param{Data} } );
    }

    my $Process = $Self->ProcessGet(
        ProcessEntityID => $Param{ProcessEntityID},
        Preferences     => 0,
    );

    if ( !$Process->{State} ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "Can't process Transition for Process '$Param{ProcessEntityID}', can't"
                . " get State out of the config!",
        );
        return;
    }

    if ( $Process->{State} eq 'Inactive' ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "Can't process Transition for Process '$Param{ProcessEntityID}',"
                . " ProcessState is '$Process->{State}'!",
        );
        return;
    }

    # We need the Processes Config
    # and the Config for the process the Ticket is in
    # and the Process has to have the 'Path' Hash set
    if (
        !IsHashRefWithData($Process)
        || !IsHashRefWithData( $Process->{Path} )
        )
    {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "Need Path for ProcessEntityID $Param{ProcessEntityID}!",
        );
        return;
    }

    # Check if our ActivitySet has a path configured
    # if it hasn't we got nothing to do -> print debuglog if desired and return
    if ( !IsHashRefWithData( $Process->{Path}->{ $Param{ActivityEntityID} } ) ) {
        if ( $Self->{Debug} > 0 ) {
            $Kernel::OM->Get('Kernel::System::Log')->Log(
                Priority => 'debug',
                Message  => 'No Path configured for Process with ID: '
                    . "$Param{ProcessEntityID} and ActivityEntityID: $Param{ActivityEntityID}!",
            );
        }
        return;
    }

    # %Transitions Hash for easier reading
    # contains all possible Transitions for the current Activity
    my %Transitions = %{ $Process->{Path}->{ $Param{ActivityEntityID} } };

    # get transition object
    my $TransitionObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::Transition');

    # Handle all possible TransitionEntityID's for the Process->Path's->ActivityEntityID down to
    # Transition.pm's TransitionCheck for validation
    # will return undef if nothing matched or the first matching TransitionEntityID
    my $TransitionEntityID = $TransitionObject->TransitionCheck(
        TransitionEntityID => [ sort { $a cmp $b } keys %Transitions ],
        Data               => \%Data,
    );

    # if we didn't get a TransitionEntityID
    # no check was successful -> return nothing
    if ( !$TransitionEntityID || !length $TransitionEntityID ) {
        if ( $Self->{Debug} > 0 ) {
            $Kernel::OM->Get('Kernel::System::Log')->Log(
                Priority => 'debug',
                Message  => 'No Transition matched for TicketID: '
                    . "$Param{TicketID} ProcessEntityID: $Param{ProcessEntityID} "
                    . "ActivityEntityID: $Param{ActivityEntityID}!",
            );
        }
        return;
    }

    # get activity object
    my $ActivityObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::Activity');

    # If we have a Transition without valid FutureActivitySet we have to complain
    if (
        !IsHashRefWithData( $Transitions{$TransitionEntityID} )
        || !$Transitions{$TransitionEntityID}->{ActivityEntityID}
        || !IsHashRefWithData(
            $ActivityObject->ActivityGet(
                Interface        => 'all',
                ActivityEntityID => $Transitions{$TransitionEntityID}->{ActivityEntityID}
            )
        )
        )
    {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "Need Target Activity for Process with "
                . "ProcessEntityID: $Param{ProcessEntityID} ActivityEntityID:"
                . " $Param{ActivityEntityID} TransitionEntityID: $TransitionEntityID!",
        );
        return;
    }

    # If we should just check what Transition matched
    # return a hash containing
    # { TransitionEntityID => FutureActivityEntityID }
    if ( $Param{CheckOnly} ) {
        if ( $Self->{Debug} > 0 ) {
            $Kernel::OM->Get('Kernel::System::Log')->Log(
                Priority => 'debug',
                Message  => "Transition with ID $TransitionEntityID matched for "
                    . "TicketID: $Param{TicketID} ProcessEntityID: $Param{ProcessEntityID} "
                    . "ActivityEntityID: $Param{ActivityEntityID}!",
            );
        }
        return { $TransitionEntityID => $Transitions{$TransitionEntityID} };
    }

    # Set the new ActivityEntityID on the Ticket
    my $Success = $Self->ProcessTicketActivitySet(
        ProcessEntityID  => $Param{ProcessEntityID},
        ActivityEntityID => $Transitions{$TransitionEntityID}->{ActivityEntityID},
        TicketID         => $Param{TicketID},
        UserID           => $Param{UserID},
    );

    if ( !$Success ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "Failed setting ActivityEntityID of Ticket: $Param{TicketID} to "
                . $Transitions{$TransitionEntityID}->{ActivityEntityID}
                . " after successful Transition: $TransitionEntityID!",
        );
        return;
    }

    # if we don't have Transition Actions on that transition,
    # return 1 for successful transition
    if (
        !$Transitions{$TransitionEntityID}->{TransitionAction}
        || (
            ref $Transitions{$TransitionEntityID}->{TransitionAction} eq 'ARRAY'
            && !@{ $Transitions{$TransitionEntityID}->{TransitionAction} }
        )
        )
    {
        return 1;
    }

    # if we have Transition Action and it isn't an array return
    if ( !IsArrayRefWithData( $Transitions{$TransitionEntityID}->{TransitionAction} ) ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "Defective Process configuration: 'TrasitionAction' must be an array in "
                . "Process: $Param{ProcessEntityID} -> Path -> "
                . "ActivityEntityID: $Param{ActivityEntityID} -> Transition: $TransitionEntityID!",
        );
        return;
    }

    # get transition action object
    my $TransitionActionObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::TransitionAction');

    my $TransitionActions = $TransitionActionObject->TransitionActionList(
        TransitionActionEntityID => $Transitions{$TransitionEntityID}->{TransitionAction},
    );

    if ( !IsArrayRefWithData($TransitionActions) ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "Getting TransitionActionList for Process: $Param{ProcessEntityID},"
                . " Transition: $TransitionEntityID failed.",
        );
        return;
    }

    for my $TransitionAction ( @{$TransitionActions} ) {

        # Refresh ticket data, as transition actions could already had modified the ticket
        #   e.g TicketServiceSet -> TicketSLASet, SLA needs to already have a Service,
        #   see bug#12147.
        %Data = $TicketObject->TicketGet(
            TicketID      => $Param{TicketID},
            DynamicFields => 1,
            UserID        => $Param{UserID},
        );

        my $TransitionActionModuleObject = $TransitionAction->{Module}->new();

        # Transition actions could replace configuration tags with actual ticket values,
        #   copying the configuration prevents unwanted results if same Transition action is called
        #   for multiple tickets, see bug#12179
        my %Config = %{ $TransitionAction->{Config} || {} };

        my $Success = $TransitionActionModuleObject->Run(
            UserID                   => $Param{UserID},
            Ticket                   => \%Data,
            ProcessEntityID          => $Param{ProcessEntityID},
            ActivityEntityID         => $Param{ActivityEntityID},
            TransitionEntityID       => $TransitionEntityID,
            TransitionActionEntityID => $TransitionAction->{TransitionActionEntityID},
            Config                   => \%Config,
        );
    }

    return 1;

}

=head2 ProcessTicketActivitySet()

    Set Ticket's ActivityEntityID

    my $Success = $ProcessObject->ProcessTicketActivitySet(
        ProcessEntityID  => 'P1',
        ActivityEntityID => 'A1',
        TicketID         => 123,
        UserID           => 123,
    );

Returns:

    $Success = 1; # undef

    1 if setting the Activity was executed
    undef if setting failed

=cut

sub ProcessTicketActivitySet {
    my ( $Self, %Param ) = @_;

    for my $Needed (qw(ProcessEntityID ActivityEntityID TicketID UserID)) {
        if ( !defined $Param{$Needed} ) {
            $Kernel::OM->Get('Kernel::System::Log')->Log(
                Priority => 'error',
                Message  => "Need $Needed!",
            );
            return;
        }
    }

    # get activity object
    my $ActivityObject = $Kernel::OM->Get('Kernel::System::ProcessManagement::Activity');

    # check on valid ActivityEntityID
    my $Success = $ActivityObject->ActivityGet(
        Interface        => 'all',
        ActivityEntityID => $Param{ActivityEntityID},
    );

    if ( !$Success ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "Process->ProcessTicketActivitySet called on "
                . "non existing ActivityEntityID: $Param{ActivityEntityID}!",
        );
        return;
    }

    # Check on valid State
    my $Process = $Self->ProcessGet(
        ProcessEntityID => $Param{ProcessEntityID},
        Preferences     => 0,
    );

    if ( !$Process->{State} ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "Can't set ActivitySet for Process '$Param{ProcessEntityID}', cat get"
                . " State out of the config!",
        );
        return;
    }

    if ( $Process->{State} eq 'Inactive' ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "Can't set ActivitySet for Process '$Param{ProcessEntityID}', State is"
                . " '$Process->{State}'!",
        );
        return;
    }

    # Get DynamicField Name that's used for storing the ActivityEntityID per ticket
    my $DynamicFieldTicketActivityEntityID
        = $Kernel::OM->Get('Kernel::Config')->Get('Process::DynamicFieldProcessManagementActivityID');
    if ( !$DynamicFieldTicketActivityEntityID ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "Need DynamicFieldProcessManagementActivityID config "
                . "for storing of ActivityEntityID on TicketID: $Param{TicketID}!",
        );
        return;
    }

    # get dynamic field objects
    my $DynamicFieldObject        = $Kernel::OM->Get('Kernel::System::DynamicField');
    my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');

    # get the dynamic fields for this screen
    my $DynamicFieldList = $DynamicFieldObject->DynamicFieldListGet(
        Valid      => 1,
        ObjectType => 'Ticket',
    );

    # Grep the Field out of the config of all Ticket DynamicFields
    my @DynamicFieldConfig = grep { $_->{Name} eq $DynamicFieldTicketActivityEntityID } @{$DynamicFieldList};

    # if the DynamicField isn't there, return 0 and log
    if ( !IsHashRefWithData( $DynamicFieldConfig[0] ) ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "DynamicField: $DynamicFieldTicketActivityEntityID not configured!",
        );
        return;
    }

    # If Ticket Update to the new ActivityEntityID was successful return 1
    if (
        $DynamicFieldBackendObject->ValueSet(
            DynamicFieldConfig => $DynamicFieldConfig[0],
            ObjectID           => $Param{TicketID},
            Value              => $Param{ActivityEntityID},
            UserID             => $Param{UserID}
        )
        )
    {
        return 1;
    }

    return;
}

=head2 ProcessTicketProcessSet()

    Set Ticket's ProcessEntityID

    my $Success = $ProcessObject->ProcessTicketProcessSet(
        ProcessEntityID => 'P1',
        TicketID        => 123,
        UserID          => 123,
    );

Returns:

    $Success = 1; # undef
    1 if setting the Activity was executed
    undef if setting failed

=cut

sub ProcessTicketProcessSet {
    my ( $Self, %Param ) = @_;

    for my $Needed (qw(ProcessEntityID TicketID UserID)) {
        if ( !defined $Param{$Needed} ) {
            $Kernel::OM->Get('Kernel::System::Log')->Log(
                Priority => 'error',
                Message  => "Need $Needed!",
            );
            return;
        }
    }

    # Check on valid ProcessEntityID
    my $Process = $Self->ProcessGet(
        ProcessEntityID => $Param{ProcessEntityID},
        Preferences     => 0,
    );

    if ( !$Process->{State} ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "Can't set Process '$Param{ProcessEntityID}' for TicketID"
                . " '$Param{TicketID}', cat get State out of the config!",
        );
        return;
    }

    if ( $Process->{State} ne 'Active' ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "Can't set Process '$Param{ProcessEntityID}' for TicketID"
                . " '$Param{TicketID}', State is '$Process->{State}'!",
        );
        return;
    }

    # Get DynamicField Name that's used for storing the ActivityEntityID per ticket
    my $DynamicFieldTicketProcessID
        = $Kernel::OM->Get('Kernel::Config')->Get('Process::DynamicFieldProcessManagementProcessID');

    if ( !$DynamicFieldTicketProcessID ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "Need DynamicFieldProcessManagementProcessID config "
                . "for storing of ProcesID on TicketID: $Param{TicketID}!",
        );
        return;
    }

    # get dynamic field objects
    my $DynamicFieldObject        = $Kernel::OM->Get('Kernel::System::DynamicField');
    my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');

    # get the dynamic fields for this screen
    my $DynamicFieldList = $DynamicFieldObject->DynamicFieldListGet(
        Valid      => 1,
        ObjectType => 'Ticket',
    );

    # Grep the Field out of the config of all Ticket DynamicFields
    my @DynamicFieldConfig = grep { $_->{Name} eq $DynamicFieldTicketProcessID } @{$DynamicFieldList};

    # if the DynamicField isn't there, return 0 and log
    if ( !IsHashRefWithData( $DynamicFieldConfig[0] ) ) {
        $Kernel::OM->Get('Kernel::System::Log')->Log(
            Priority => 'error',
            Message  => "DynamicField: $DynamicFieldTicketProcessID not configured!",
        );
        return;
    }

    # If Ticket Update to the new ActivityEntityID was successful return 1
    if (
        $DynamicFieldBackendObject->ValueSet(
            DynamicFieldConfig => $DynamicFieldConfig[0],
            ObjectID           => $Param{TicketID},
            Value              => $Param{ProcessEntityID},
            UserID             => $Param{UserID},
        )
        )
    {
        return 1;
    }

    return;
}

=head2 ProcessPreferencesSet()

Sets process preferences.

    $ProcessObject->ProcessPreferencesSet(
        ProcessEntityID => 123,
        Key             => 'UserComment',
        Value           => 'some comment',
        UserID          => 123,
    );

=cut

sub ProcessPreferencesSet {
    my ( $Self, %Param ) = @_;

    return $Kernel::OM->Get('Kernel::System::ProcessManagement::DB::Process')->ProcessPreferencesSet(%Param);
}

=head2 ProcessPreferencesDelete()

Deletes process preferences.

    $ProcessObject->ProcessPreferencesDelete(
        ProcessEntityID => 123,
        Key             => 'UserComment',
    );

=cut

sub ProcessPreferencesDelete {
    my ( $Self, %Param ) = @_;

    return $Kernel::OM->Get('Kernel::System::ProcessManagement::DB::Process')->ProcessPreferencesDelete(%Param);
}

=head2 ProcessPreferencesGet()

Gets process preferences.

    my %Preferences = $ProcessObject->ProcessPreferencesGet(
        ProcessEntityID => 123,
        Export          => 1,       # optional, if set to 1, the content of a file will be exported as Base64
        UserID          => 123,
    );

Return:

    my %Preferences = (
        'UserComment' => 'some comment',
    );

=cut

sub ProcessPreferencesGet {
    my ( $Self, %Param ) = @_;

    return $Kernel::OM->Get('Kernel::System::ProcessManagement::DB::Process')->ProcessPreferencesGet(%Param);
}

1;

=head1 TERMS AND CONDITIONS

This software is part of the OTRS project (L<https://otrs.org/>).

This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (GPL). If you
did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.

=cut
