Files
scripts/Perl OTRS/Kernel/Modules/AgentITSMChangePrint.pm
2024-10-14 00:08:40 +02:00

1482 lines
45 KiB
Perl

# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# 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::Modules::AgentITSMChangePrint;
use strict;
use warnings;
use List::Util qw(max);
use Kernel::Language qw(Translatable);
use Kernel::System::VariableCheck qw(:all);
our $ObjectManagerDisabled = 1;
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {%Param};
bless( $Self, $Type );
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
# get param object
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
# Find out whether a change or a workorder should be printed.
# A workorder is to be printed when the WorkOrderID is passed.
# Otherwise a change should be printed.
my $WorkOrderID = $ParamObject->GetParam( Param => 'WorkOrderID' );
my $PrintWorkOrder = $WorkOrderID ? 1 : 0;
my $PrintChange = !$WorkOrderID;
my $WorkOrder = {};
my $ChangeID;
# get needed objects
my $ChangeObject = $Kernel::OM->Get('Kernel::System::ITSMChange');
my $WorkOrderObject = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMWorkOrder');
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
# get config of frontend module
$Self->{Config} = $ConfigObject->Get("ITSMChange::Frontend::$Self->{Action}");
if ($PrintWorkOrder) {
# check permission on the workorder
my $Access = $WorkOrderObject->Permission(
Type => $Self->{Config}->{Permission},
Action => $Self->{Action},
WorkOrderID => $WorkOrderID,
UserID => $Self->{UserID},
);
# error screen
if ( !$Access ) {
return $LayoutObject->NoPermission(
Message => $LayoutObject->{LanguageObject}
->Translate( 'You need %s permissions!', $Self->{Config}->{Permission} ),
WithHeader => 'yes',
);
}
# get workorder information
$WorkOrder = $WorkOrderObject->WorkOrderGet(
WorkOrderID => $WorkOrderID,
UserID => $Self->{UserID},
);
# check error
if ( !$WorkOrder ) {
return $LayoutObject->ErrorScreen(
Message =>
$LayoutObject->{LanguageObject}->Translate( 'WorkOrder "%s" not found in database!', $WorkOrderID ),
Comment => Translatable('Please contact the administrator.'),
);
}
# infer the change id from the workorder
$ChangeID = $WorkOrder->{ChangeID};
if ( !$ChangeID ) {
# error page
return $LayoutObject->ErrorScreen(
Message => Translatable('Can\'t create output, as the workorder is not attached to a change!'),
Comment => Translatable('Please contact the administrator.'),
);
}
}
else {
# the change id is required, as we have no workorder id
$ChangeID = $ParamObject->GetParam( Param => 'ChangeID' );
if ( !$ChangeID ) {
# error page
return $LayoutObject->ErrorScreen(
Message => Translatable('Can\'t create output, as no ChangeID is given!'),
Comment => Translatable('Please contact the administrator.'),
);
}
# check permission on the change
my $Access = $ChangeObject->Permission(
Type => $Self->{Config}->{Permission},
Action => $Self->{Action},
ChangeID => $ChangeID,
UserID => $Self->{UserID},
);
# error screen
if ( !$Access ) {
return $LayoutObject->NoPermission(
Message => $LayoutObject->{LanguageObject}
->Translate( 'You need %s permissions!', $Self->{Config}->{Permission} ),
WithHeader => 'yes',
);
}
}
# get change information
my $Change = $ChangeObject->ChangeGet(
ChangeID => $ChangeID,
UserID => $Self->{UserID},
);
# check error
if ( !$Change ) {
return $LayoutObject->ErrorScreen(
Message => $LayoutObject->{LanguageObject}->Translate( 'Change "%s" not found in database!', $ChangeID ),
Comment => Translatable('Please contact the administrator.'),
);
}
# some init for PDF-Output
# page controls the PDF-generation
$Self->{Page} = {};
my $Page = $Self->{Page};
# get maximum number of pages
$Page->{MaxPages} = $ConfigObject->Get('PDF::MaxPages');
if ( !$Page->{MaxPages} || $Page->{MaxPages} < 1 || $Page->{MaxPages} > 1000 ) {
$Page->{MaxPages} = 100;
}
# page layout settings
$Page->{MarginTop} = 30;
$Page->{MarginRight} = 40;
$Page->{MarginBottom} = 40;
$Page->{MarginLeft} = 40;
# the second item in the page title is the area in the product 'ITSM Change Management'
my $HeaderArea = $PrintChange ? 'ITSM Change' : 'ITSM Workorder';
$HeaderArea = $LayoutObject->{LanguageObject}->Translate($HeaderArea);
# the last item in the page title is either the change number of the full workorder number
my $HeaderValue = $PrintChange
?
$Change->{ChangeNumber}
:
join( '-', $Change->{ChangeNumber}, $WorkOrder->{WorkOrderNumber} );
# start the document
my $Output = $Self->_StartDocument(
HeaderArea => $HeaderArea,
HeaderValue => $HeaderValue,
);
# get link object
my $LinkObject = $Kernel::OM->Get('Kernel::System::LinkObject');
# the link types are needed for showing the linked objects
my %LinkTypeList = $LinkObject->TypeList(
UserID => $Self->{UserID},
);
# print the change specific stuff
if ($PrintChange) {
# start the first page
$Output .= $Self->_OutputHeadline(
HeaderArea => $HeaderArea,
HeaderValue => $HeaderValue,
Title => $Change->{ChangeTitle} || Translatable('unknown change title'),
TemplatePrefix => 'Change',
);
# output change info
$Output .= $Self->_OutputChangeInfo(
Change => $Change,
PrintWorkOrder => $PrintWorkOrder,
);
# output change description and justification
# the plain content will be displayed
for my $Attribute (qw(Description Justification)) {
$Output .= $Self->_OutputLongText(
PrintChange => $PrintChange,
PrintWorkOrder => $PrintWorkOrder,
Title =>
$LayoutObject->{LanguageObject}->Translate($Attribute),
LongText => $Change->{ $Attribute . 'Plain' },
);
}
# get linked objects which are directly linked with this change object
my $LinkListWithData = $LinkObject->LinkListWithData(
Object => 'ITSMChange',
Key => $ChangeID,
State => 'Valid',
UserID => $Self->{UserID},
);
# get the combined linked objects from all workorders of this change
my $LinkListWithDataCombinedWorkOrders = {};
for my $WorkOrderID ( @{ $Change->{WorkOrderIDs} } ) {
# get linked objects of this workorder
my $LinkListWithDataWorkOrder = $LinkObject->LinkListWithData(
Object => 'ITSMWorkOrder',
Key => $WorkOrderID,
State => 'Valid',
UserID => $Self->{UserID},
);
OBJECT:
for my $Object ( sort keys %{$LinkListWithDataWorkOrder} ) {
# only show linked services and config items of workorder
if ( $Object ne 'Service' && $Object ne 'ITSMConfigItem' ) {
next OBJECT;
}
LINKTYPE:
for my $LinkType ( sort keys %{ $LinkListWithDataWorkOrder->{$Object} } ) {
DIRECTION:
for my $Direction (
sort keys %{ $LinkListWithDataWorkOrder->{$Object}->{$LinkType} }
)
{
ID:
for my $ID (
sort keys %{
$LinkListWithDataWorkOrder->{$Object}->{$LinkType}->{$Direction}
}
)
{
# combine the linked object data from all workorders
$LinkListWithDataCombinedWorkOrders->{$Object}->{$LinkType}
->{$Direction}->{$ID} = $LinkListWithDataWorkOrder->{$Object}->{$LinkType}->{$Direction}
->{$ID};
}
}
}
}
}
# add combined linked objects from workorder to linked objects from change object
$LinkListWithData = {
%{$LinkListWithData},
%{$LinkListWithDataCombinedWorkOrders},
};
# get the link data
if ( $LinkListWithData && ref $LinkListWithData eq 'HASH' && %{$LinkListWithData} ) {
my %LinkData = $LayoutObject->LinkObjectTableCreate(
LinkListWithData => $LinkListWithData,
ViewMode => 'SimpleRaw',
);
$Output .= $Self->_OutputLinkedObjects(
PrintChange => $PrintChange,
PrintWorkOrder => $PrintWorkOrder,
LinkData => \%LinkData,
LinkTypeList => \%LinkTypeList,
);
}
# output an overview over workorders
my @WorkOrderOverview;
for my $WorkOrderID ( @{ $Change->{WorkOrderIDs} } ) {
# get workorder info
my $WorkOrder = $WorkOrderObject->WorkOrderGet(
WorkOrderID => $WorkOrderID,
UserID => $Self->{UserID},
);
# check error
if ( !$WorkOrder ) {
return $LayoutObject->ErrorScreen(
Message => $LayoutObject->{LanguageObject}
->Translate( 'WorkOrder "%s" not found in database!', $WorkOrderID ),
Comment => Translatable('Please contact the administrator.'),
);
}
push @WorkOrderOverview, [
$WorkOrder->{WorkOrderNumber},
$WorkOrder->{WorkOrderTitle},
$WorkOrder->{WorkOrderState},
$WorkOrder->{PlannedStartTime},
$WorkOrder->{PlannedEndTime},
$WorkOrder->{ActualStartTime},
$WorkOrder->{ActualEndTime},
];
}
$Output .= $Self->_OutputWorkOrderOverview(
WorkOrderOverview => \@WorkOrderOverview,
);
}
# output either a single workorder or all workorders of a change
my @WorkOrderIDs = $PrintChange ? @{ $Change->{WorkOrderIDs} || [] } : ($WorkOrderID);
for my $WorkOrderID (@WorkOrderIDs) {
# get workorder info
my $WorkOrder = $WorkOrderObject->WorkOrderGet(
WorkOrderID => $WorkOrderID,
UserID => $Self->{UserID},
);
# check error
if ( !$WorkOrder ) {
return $LayoutObject->ErrorScreen(
Message => $LayoutObject->{LanguageObject}->Translate(
'WorkOrder "%s" not found in database!',
$WorkOrderID,
),
Comment => Translatable('Please contact the administrator.'),
);
}
# start a new page for every workorder
my $HeaderArea = $LayoutObject->{LanguageObject}->Translate('ITSM Workorder');
my $HeaderValue = join '-', $Change->{ChangeNumber}, $WorkOrder->{ Translatable('WorkOrderNumber') };
$Output .= $Self->_OutputHeadline(
HeaderArea => $HeaderArea,
HeaderValue => $HeaderValue,
Title => $WorkOrder->{ Translatable('WorkOrderTitle') } || Translatable('unknown workorder title'),
TemplatePrefix => 'WorkOrder',
);
$Output .= $Self->_OutputWorkOrderInfo(
Change => $Change,
WorkOrder => $WorkOrder,
);
# output workorder instruction and report
# The plain content will be displayed
for my $Attribute (qw(Instruction Report)) {
$Output .= $Self->_OutputLongText(
PrintChange => 0,
PrintWorkOrder => 1,
Title =>
$LayoutObject->{LanguageObject}->Translate($Attribute),
LongText => $WorkOrder->{ $Attribute . 'Plain' },
);
}
# get linked objects
my $LinkListWithData = $LinkObject->LinkListWithData(
Object => 'ITSMWorkOrder',
Key => $WorkOrderID,
State => 'Valid',
UserID => $Self->{UserID},
);
# get the link data
if ( $LinkListWithData && ref $LinkListWithData eq 'HASH' && %{$LinkListWithData} ) {
my %LinkData = $LayoutObject->LinkObjectTableCreate(
LinkListWithData => $LinkListWithData,
ViewMode => 'SimpleRaw',
);
$Output .= $Self->_OutputLinkedObjects(
PrintChange => 0,
PrintWorkOrder => 1,
LinkData => \%LinkData,
LinkTypeList => \%LinkTypeList,
);
}
}
# generate PDF output
# get time object
# generate a filename
my $CurSysDTObject = $Kernel::OM->Create('Kernel::System::DateTime');
my @Filename;
if ($PrintChange) {
push @Filename, 'change', $Change->{ChangeNumber};
}
else {
push @Filename, 'workorder', $Change->{ChangeNumber} . '-' . $WorkOrder->{WorkOrderNumber};
}
push @Filename, $CurSysDTObject->Format(
Format => '%F_%H-%M',
);
# return the PDF document
my $PDFString = $Kernel::OM->Get('Kernel::System::PDF')->DocumentOutput();
return $LayoutObject->Attachment(
Filename => join( '_', @Filename ) . '.pdf',
ContentType => 'application/pdf',
Content => $PDFString,
Type => 'inline',
);
}
# start the document
sub _StartDocument {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Needed (qw(HeaderArea HeaderValue)) {
if ( !defined( $Param{$Needed} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
# get layout object
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
# title of the PDF-Document
my $Product = $Kernel::OM->Get('Kernel::Config')->Get('Product');
my $Title = sprintf '%s: %s#%s', $Product, $Param{HeaderArea}, $Param{HeaderValue};
# create new PDF document
$Kernel::OM->Get('Kernel::System::PDF')->DocumentNew(
Title => $Title,
Encode => $LayoutObject->{UserCharset},
);
return '';
}
# output the headline, create a new page
sub _OutputHeadline {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Needed (qw(HeaderArea HeaderValue Title TemplatePrefix)) {
if ( !defined( $Param{$Needed} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
# get layout object
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $PrintedBy = $LayoutObject->{LanguageObject}->Translate('printed by');
my $Time = $LayoutObject->{Time};
my $UserFullName = $Kernel::OM->Get('Kernel::System::User')->UserName(
UserID => $Self->{UserID},
);
# page controls the PDF-generation
# page headers and footer
my $Page = $Self->{Page};
$Page->{HeaderRight} = sprintf '%s#%s', $Param{HeaderArea}, $Param{HeaderValue};
$Page->{PageText} = $LayoutObject->{LanguageObject}->Translate('Page');
$Page->{PageCount} = $Self->{Page}->{PageCount} // 1;
# create new PDF page
$Kernel::OM->Get('Kernel::System::PDF')->PageNew(
%{$Page},
FooterRight => $Page->{PageText} . ' ' . $Page->{PageCount},
);
$Self->{Page}->{PageCount}++;
# get PDF object
my $PDFObject = $Kernel::OM->Get('Kernel::System::PDF');
$PDFObject->PositionSet(
Move => 'relativ',
Y => -6,
);
# output title
$PDFObject->Text(
Text => $Param{Title},
FontSize => 13,
);
$PDFObject->PositionSet(
Move => 'relativ',
Y => -6,
);
# output "printed by"
$PDFObject->Text(
Text => $PrintedBy . ' '
. $UserFullName . ' '
. $Time,
FontSize => 9,
);
$PDFObject->PositionSet(
Move => 'relativ',
Y => -14,
);
return '';
}
# a helper for preparing a table row for PDF generation
sub _PrepareAndAddInfoRow {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Needed (qw(RowSpec Data)) {
if ( !defined( $Param{$Needed} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
my ( $RowSpec, $Data ) = @Param{qw(RowSpec Data)};
# short name, just for convenience
my $Attribute = $RowSpec->{Attribute};
# get config of frontend module
$Self->{Config} = $Kernel::OM->Get('Kernel::Config')->Get("ITSMChange::Frontend::$Self->{Action}");
# skip if row is switched off in SysConfig
return if $RowSpec->{IsOptional} && !$Self->{Config}->{$Attribute};
# get layout object
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
# keys are always translatable
my $Key = $RowSpec->{Key} || $Attribute;
$Key = $LayoutObject->{LanguageObject}->Translate($Key);
# determine the value
my $Value;
if ( $RowSpec->{ValueIsTime} ) {
# format the time value
$Value = $LayoutObject->Output(
Template => '[% Data.' . $Attribute . ' | Localize("TimeLong") %]',
Data => $Data,
);
}
elsif ( $RowSpec->{ValueIsUser} ) {
# format the user id
if ( $Data->{ $Attribute . 'ID' } ) {
my $UserFullName = $Kernel::OM->Get('Kernel::System::User')->UserName(
UserID => $Data->{ $Attribute . 'ID' },
);
if ($UserFullName) {
$Value = $UserFullName;
}
else {
$Value = 'ID=' . $Data->{$Attribute};
}
}
}
elsif ( $RowSpec->{ValueIsDynamicField} ) {
$Value = $RowSpec->{Value};
}
else {
# take value from the passed in data
$Value = $Data->{$Attribute};
}
# translate the value
if ( $Value && $RowSpec->{ValueIsTranslatable} ) {
$Value = $LayoutObject->{LanguageObject}->Translate($Value);
}
# add separator between key and value
$Key .= ':';
# show row
push @{ $RowSpec->{Table} },
{
Key => $Key,
Value => $Value,
};
return;
}
# emit information about a change
sub _OutputChangeInfo {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Needed (qw(Change PrintWorkOrder)) {
if ( !defined( $Param{$Needed} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
# just for having shorter names
my $Change = $Param{Change};
# fill the two tables on top,
# both tables have two colums: Key and Value
my ( @TableLeft, @TableRight );
# determine values that can't easily be determined in _PrepareAndAddInfoRow()
my %ComplicatedValue;
# Values for CAB
for my $Attribute (qw(CABAgents CABCustomers)) {
my @LongNames;
if ( $Attribute eq 'CABAgents' && $Change->{$Attribute} ) {
for my $CABAgent ( @{ $Change->{$Attribute} } ) {
my $UserFullName = $Kernel::OM->Get('Kernel::System::User')->UserName(
UserID => $CABAgent,
);
if ($UserFullName) {
push @LongNames, $UserFullName;
}
else {
push @LongNames, 'ID=' . $CABAgent;
}
}
}
elsif ( $Attribute eq 'CABCustomers' && $Change->{$Attribute} ) {
for my $CABCustomer ( @{ $Change->{$Attribute} } ) {
my %UserData = $Kernel::OM->Get('Kernel::System::CustomerUser')->CustomerUserDataGet(
User => $CABCustomer,
Cache => 1,
);
if (%UserData) {
push @LongNames, $UserData{UserFullname};
}
else {
push @LongNames, 'ID=' . $CABCustomer;
}
}
}
# remember the value
$ComplicatedValue{ $Attribute . 'Long' } = join( "\n", @LongNames ) || '-';
}
# value for attachments
{
# get change object
my $ChangeObject = $Kernel::OM->Get('Kernel::System::ITSMChange');
my @Attachments = $ChangeObject->ChangeAttachmentList(
ChangeID => $Change->{ChangeID},
);
my @Values;
ATTACHMENT:
for my $Filename (@Attachments) {
# get info about file
my $AttachmentData = $ChangeObject->ChangeAttachmentGet(
ChangeID => $Change->{ChangeID},
Filename => $Filename,
);
# check for attachment information
next ATTACHMENT if !$AttachmentData;
push @Values, sprintf '%s %s',
$AttachmentData->{Filename},
$AttachmentData->{Filesize};
}
# show row
$ComplicatedValue{Attachments} = join( "\n", @Values ) || '-';
}
# get the dynamic fields for this screen
my $DynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
Valid => 1,
ObjectType => [ 'ITSMChange', 'ITSMWorkOrder' ],
FieldFilter => $Self->{Config}->{DynamicField} || {},
);
# get layout object
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
# cycle trough the activated Dynamic Fields
my @DynamicFieldRowSpec;
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{$DynamicField} ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
# show only change dynamic fields here
next DYNAMICFIELD if $DynamicFieldConfig->{ObjectType} ne 'ITSMChange';
# get dynamic field backend object
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
my $Value = $DynamicFieldBackendObject->ValueGet(
DynamicFieldConfig => $DynamicFieldConfig,
ObjectID => $Change->{ChangeID},
);
# get print string for this dynamic field
my $ValueStrg = $DynamicFieldBackendObject->DisplayValueRender(
DynamicFieldConfig => $DynamicFieldConfig,
Value => $Value,
ValueMaxChars => 1000,
LayoutObject => $LayoutObject,
);
# for empty values
if ( !$ValueStrg->{Value} ) {
$ValueStrg->{Value} = '-';
}
my $Label = $DynamicFieldConfig->{Label};
push @DynamicFieldRowSpec, {
Attribute => $Label,
Key => $Label,
ValueIsDynamicField => 1,
Value => $ValueStrg->{Value},
Table => \@TableLeft,
};
}
my @RowSpec = (
{
Attribute => Translatable('ChangeState'),
Table => \@TableLeft,
ValueIsTranslatable => 1,
},
{
Attribute => Translatable('PlannedEffort'),
IsOptional => 1,
Table => \@TableLeft,
},
{
Attribute => Translatable('AccountedTime'),
IsOptional => 1,
Table => \@TableLeft,
},
{
Attribute => Translatable('Category'),
Key => 'Category',
Table => \@TableLeft,
ValueIsTranslatable => 1,
},
{
Attribute => Translatable('Impact'),
Key => 'Impact',
Table => \@TableLeft,
ValueIsTranslatable => 1,
},
{
Attribute => Translatable('Priority'),
Key => 'Priority',
Table => \@TableLeft,
ValueIsTranslatable => 1,
},
@DynamicFieldRowSpec,
{
Attribute => Translatable('ChangeManager'),
Table => \@TableLeft,
ValueIsUser => 1,
},
{
Attribute => Translatable('ChangeBuilder'),
Table => \@TableLeft,
ValueIsUser => 1,
},
{
Attribute => 'CABAgentsLong',
Key => Translatable('CAB Agents'),
Table => \@TableLeft,
},
{
Attribute => 'CABCustomersLong',
Key => Translatable('CAB Customers'),
Table => \@TableLeft,
},
{
Attribute => 'Attachments',
Key => 'Attachments',
Table => \@TableLeft,
},
{
Attribute => Translatable('RequestedTime'),
IsOptional => 1,
Table => \@TableRight,
ValueIsTime => 1,
},
{
Attribute => Translatable('PlannedStartTime'),
Table => \@TableRight,
ValueIsTime => 1,
},
{
Attribute => Translatable('PlannedEndTime'),
Table => \@TableRight,
ValueIsTime => 1,
},
{
Attribute => Translatable('ActualStartTime'),
Table => \@TableRight,
ValueIsTime => 1,
},
{
Attribute => Translatable('ActualEndTime'),
Table => \@TableRight,
ValueIsTime => 1,
},
{
Attribute => Translatable('CreateTime'),
Key => 'Created',
Table => \@TableRight,
ValueIsTime => 1,
},
{
Attribute => Translatable('ChangeTime'),
Key => 'Changed',
Table => \@TableRight,
ValueIsTime => 1,
},
);
for my $RowSpec (@RowSpec) {
# fill @TableLeft and @TableRight
$Self->_PrepareAndAddInfoRow(
RowSpec => $RowSpec,
Data => { %{$Change}, %ComplicatedValue },
);
}
# number of rows in the change info table
my $Rows = max( scalar(@TableLeft), scalar(@TableRight) );
my %Table;
for my $Row ( 0 .. $Rows - 1 ) {
$Table{CellData}[$Row][0]{Content} = $TableLeft[$Row]->{Key};
$Table{CellData}[$Row][0]{Font} = 'ProportionalBold';
$Table{CellData}[$Row][1]{Content} = $TableLeft[$Row]->{Value};
$Table{CellData}[$Row][2]{Content} = ' ';
$Table{CellData}[$Row][2]{BackgroundColor} = '#FFFFFF';
$Table{CellData}[$Row][3]{Content} = $TableRight[$Row]->{Key};
$Table{CellData}[$Row][3]{Font} = 'ProportionalBold';
$Table{CellData}[$Row][4]{Content} = $TableRight[$Row]->{Value};
}
$Table{ColumnData}[0]{Width} = 80;
$Table{ColumnData}[1]{Width} = 170.5;
$Table{ColumnData}[2]{Width} = 4;
$Table{ColumnData}[3]{Width} = 80;
$Table{ColumnData}[4]{Width} = 170.5;
$Table{Type} = 'Cut';
$Table{Border} = 0;
$Table{FontSize} = 6;
$Table{BackgroundColorEven} = '#DDDDDD';
$Table{Padding} = 1;
$Table{PaddingTop} = 3;
$Table{PaddingBottom} = 3;
# output table
$Self->_PDFOutputTable(
Table => \%Table,
);
return '';
}
# emit information about a workorder
sub _OutputWorkOrderInfo {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Needed (qw(Change WorkOrder)) {
if ( !defined( $Param{$Needed} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
my ( $WorkOrder, $Change ) = @Param{qw(WorkOrder Change)};
my $PrintWorkOrder = $Param{PrintWorkOrder} || 0;
# fill the two tables on top,
# both tables have two colums: Key and Value
my ( @TableLeft, @TableRight );
# determine values that can't be determined in _PrepareAndAddInfoRow()
my %ComplicatedValue;
# value for attachments
{
# get work order object
my $WorkOrderObject = $Kernel::OM->Get('Kernel::System::ITSMChange::ITSMWorkOrder');
my @Attachments = $WorkOrderObject->WorkOrderAttachmentList(
WorkOrderID => $WorkOrder->{WorkOrderID},
);
my @Values;
ATTACHMENT:
for my $Filename (@Attachments) {
# get info about file
my $AttachmentData = $WorkOrderObject->WorkOrderAttachmentGet(
WorkOrderID => $WorkOrder->{WorkOrderID},
Filename => $Filename,
);
# check for attachment information
next ATTACHMENT if !$AttachmentData;
push @Values, sprintf '%s %s',
$AttachmentData->{Filename},
$AttachmentData->{Filesize};
}
# show row
$ComplicatedValue{Attachments} = join( "\n", @Values ) || '-';
}
# allow wrapping of long words in the change title
( $ComplicatedValue{WrappableChangeTitle} = $Change->{ChangeTitle} )
=~ s{ ( \S{25} ) }{$1 }xmsg;
# get config of frontend module
$Self->{Config} = $Kernel::OM->Get('Kernel::Config')->Get("ITSMChange::Frontend::$Self->{Action}");
# get the dynamic fields for this screen
my $DynamicField = $Kernel::OM->Get('Kernel::System::DynamicField')->DynamicFieldListGet(
Valid => 1,
ObjectType => [ 'ITSMChange', 'ITSMWorkOrder' ],
FieldFilter => $Self->{Config}->{DynamicField} || {},
);
# get layout object
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my @DynamicFieldRowSpec;
DYNAMICFIELD:
for my $DynamicFieldConfig ( @{$DynamicField} ) {
next DYNAMICFIELD if !IsHashRefWithData($DynamicFieldConfig);
# show only workorder dynamic fields here
next DYNAMICFIELD if $DynamicFieldConfig->{ObjectType} ne 'ITSMWorkOrder';
# get dynamic field backend object
my $DynamicFieldBackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
my $Value = $DynamicFieldBackendObject->ValueGet(
DynamicFieldConfig => $DynamicFieldConfig,
ObjectID => $WorkOrder->{WorkOrderID},
);
# get print string for this dynamic field
my $ValueStrg = $DynamicFieldBackendObject->DisplayValueRender(
DynamicFieldConfig => $DynamicFieldConfig,
Value => $Value,
ValueMaxChars => 1000,
LayoutObject => $LayoutObject,
);
# for empty values
if ( !$ValueStrg->{Value} ) {
$ValueStrg->{Value} = '-';
}
my $Label = $DynamicFieldConfig->{Label};
push @DynamicFieldRowSpec, {
Attribute => $Label,
Key => $Label,
ValueIsDynamicField => 1,
Value => $ValueStrg->{Value},
Table => \@TableLeft,
};
}
my @RowSpec = (
{
Attribute => 'WrappableChangeTitle',
Table => \@TableLeft,
Key => 'ChangeTitle',
},
{
Attribute => Translatable('ChangeNumber'),
Table => \@TableLeft,
Key => 'ChangeNumber',
},
{
Attribute => Translatable('WorkOrderState'),
Table => \@TableLeft,
ValueIsTranslatable => 1,
},
{
Attribute => Translatable('WorkOrderType'),
Table => \@TableLeft,
ValueIsTranslatable => 1,
},
{
Attribute => Translatable('WorkOrderAgent'),
Table => \@TableLeft,
ValueIsUser => 1,
},
{
Attribute => Translatable('PlannedEffort'),
IsOptional => 1,
Table => \@TableLeft,
Key => 'PlannedEffort',
},
{
Attribute => Translatable('AccountedTime'),
IsOptional => 1,
Table => \@TableLeft,
Key => 'AccountedTime',
},
@DynamicFieldRowSpec,
{
Attribute => Translatable('Attachments'),
Key => 'Attachments',
Table => \@TableLeft,
},
{
Attribute => Translatable('PlannedStartTime'),
Table => \@TableRight,
ValueIsTime => 1,
Key => 'PlannedStartTime',
},
{
Attribute => Translatable('PlannedEndTime'),
Table => \@TableRight,
ValueIsTime => 1,
Key => 'PlannedEndTime',
},
{
Attribute => Translatable('ActualStartTime'),
Table => \@TableRight,
ValueIsTime => 1,
Key => 'ActualStartTime',
},
{
Attribute => Translatable('ActualEndTime'),
Table => \@TableRight,
ValueIsTime => 1,
Key => 'ActualEndTime',
},
{
Attribute => Translatable('CreateTime'),
Key => 'Created',
Table => \@TableRight,
ValueIsTime => 1,
},
{
Attribute => Translatable('ChangeTime'),
Key => 'Changed',
Table => \@TableRight,
ValueIsTime => 1,
},
);
for my $RowSpec (@RowSpec) {
# fill @TableLeft and @TableRight
# the workorder data overrides the change data
$Self->_PrepareAndAddInfoRow(
RowSpec => $RowSpec,
Data => { %{$Change}, %{$WorkOrder}, %ComplicatedValue },
);
}
my $Rows = max( scalar(@TableLeft), scalar(@TableRight) );
my %Table;
for my $Row ( 0 .. $Rows - 1 ) {
$Table{CellData}[$Row][0]{Content} = $TableLeft[$Row]->{Key};
$Table{CellData}[$Row][0]{Font} = 'ProportionalBold';
$Table{CellData}[$Row][1]{Content} = $TableLeft[$Row]->{Value};
$Table{CellData}[$Row][2]{Content} = ' ';
$Table{CellData}[$Row][2]{BackgroundColor} = '#FFFFFF';
$Table{CellData}[$Row][3]{Content} = $TableRight[$Row]->{Key};
$Table{CellData}[$Row][3]{Font} = 'ProportionalBold';
$Table{CellData}[$Row][4]{Content} = $TableRight[$Row]->{Value};
}
$Table{ColumnData}[0]{Width} = 80;
$Table{ColumnData}[1]{Width} = 170.5;
$Table{ColumnData}[2]{Width} = 4;
$Table{ColumnData}[3]{Width} = 80;
$Table{ColumnData}[4]{Width} = 170.5;
$Table{Type} = 'Cut';
$Table{Border} = 0;
$Table{FontSize} = 6;
$Table{BackgroundColorEven} = '#DDDDDD';
$Table{Padding} = 1;
$Table{PaddingTop} = 3;
$Table{PaddingBottom} = 3;
# output table
$Self->_PDFOutputTable(
Table => \%Table,
);
return '';
}
# output a body of text, such as a change description
sub _OutputLongText {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Needed (qw(PrintChange PrintWorkOrder Title LongText)) {
if ( !defined( $Param{$Needed} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!",
);
return;
}
}
# get PDF object
my $PDFObject = $Kernel::OM->Get('Kernel::System::PDF');
# some vertical whitespace
$PDFObject->PositionSet(
Move => 'relativ',
Y => -15,
);
# output headline for the section
$PDFObject->Text(
Text => $Param{Title},
Height => 7,
Type => 'Cut',
Font => 'ProportionalBoldItalic',
FontSize => 7,
Color => '#666666',
);
# vertical whitespace after title
$PDFObject->PositionSet(
Move => 'relativ',
Y => -4,
);
# table params common to printing a body of text,
# actually a table is a bit of overkill for a single text,
my %Table = (
Type => 'Cut',
Border => 0,
Font => 'Monospaced',
FontSize => 7,
BackgroundColor => '#DDDDDD',
Padding => 4,
PaddingTop => 8,
PaddingBottom => 8,
);
# output tables
$Table{CellData}[0][0]{Content} = $Param{LongText} || '';
# output table
$Self->_PDFOutputTable(
Table => \%Table,
);
return '';
}
# output overview over workorders
sub _OutputWorkOrderOverview {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !defined( $Param{WorkOrderOverview} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need WorkOrderOverview!"
);
return;
}
# get PDF object
my $PDFObject = $Kernel::OM->Get('Kernel::System::PDF');
# vertical whitespace before section headline
$PDFObject->PositionSet(
Move => 'relativ',
Y => -15,
);
# get laytout object
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
# output headline for the section
my $Translation = $LayoutObject->{LanguageObject};
my $SectionTitle =
$Translation->Translate( 'ITSM Workorder Overview (%s)', scalar @{ $Param{WorkOrderOverview} } );
$PDFObject->Text(
Text => $SectionTitle,
Height => 7,
Type => 'Cut',
Font => 'ProportionalBoldItalic',
FontSize => 7,
Color => '#666666',
);
# vertical whitespace after section headline
$PDFObject->PositionSet(
Move => 'relativ',
Y => -4,
);
# output the overview table only if there is at least a single workorder,
# printing an empty table might create havoc
if ( @{ $Param{WorkOrderOverview} } ) {
my %Table;
my $Row = 0;
# add table header
$Table{CellData}[ $Row++ ] = [
{
Font => 'ProportionalBold',
Content => '#',
},
{
Font => 'ProportionalBold',
Content => $Translation->Translate('Title'),
},
{
Font => 'ProportionalBold',
Content => $Translation->Translate('State'),
},
{
Font => 'ProportionalBold',
Content => $Translation->Translate('PlannedStartTime'),
},
{
Font => 'ProportionalBold',
Content => $Translation->Translate('PlannedEndTime'),
},
{
Font => 'ProportionalBold',
Content => $Translation->Translate('ActualStartTime'),
},
{
Font => 'ProportionalBold',
Content => $Translation->Translate('ActualEndTime'),
},
];
for my $WorkOrder ( @{ $Param{WorkOrderOverview} } ) {
$Table{CellData}[ $Row++ ] = [ map { { Content => $_ } } @{$WorkOrder} ];
}
$Table{ColumnData}[0]{Width} = 2;
$Table{ColumnData}[1]{Width} = 63;
$Table{ColumnData}[2]{Width} = 25;
$Table{ColumnData}[3]{Width} = 40;
$Table{ColumnData}[4]{Width} = 40;
$Table{ColumnData}[5]{Width} = 40;
$Table{ColumnData}[6]{Width} = 40;
# table params
$Table{Type} = 'Cut';
$Table{Border} = 0;
$Table{FontSize} = 6;
$Table{BackgroundColor} = '#DDDDDD';
$Table{Padding} = 1;
$Table{PaddingTop} = 3;
$Table{PaddingBottom} = 3;
# output table
$Self->_PDFOutputTable(
Table => \%Table,
);
}
return 1;
}
# output info about linked objects of a change or a workorder
sub _OutputLinkedObjects {
my ( $Self, %Param ) = @_;
# check needed stuff
for my $Needed (qw(PrintChange PrintWorkOrder LinkData LinkTypeList)) {
if ( !defined( $Param{$Needed} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need $Needed!"
);
return;
}
}
# get layout object
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my %TypeList = %{ $Param{LinkTypeList} };
my %Table;
my $Row = 0;
for my $LinkTypeLinkDirection ( sort { lc $a cmp lc $b } keys %{ $Param{LinkData} } ) {
# investigate link type name
my @LinkData = split q{::}, $LinkTypeLinkDirection;
my $LinkTypeName = $TypeList{ $LinkData[0] }->{ $LinkData[1] . 'Name' };
$LinkTypeName = $LayoutObject->{LanguageObject}->Translate($LinkTypeName);
# define headline
$Table{CellData}[$Row][0]{Content} = $LinkTypeName . ':';
$Table{CellData}[$Row][0]{Font} = 'ProportionalBold';
$Table{CellData}[$Row][1]{Content} = '';
# extract object list
my $ObjectList = $Param{LinkData}->{$LinkTypeLinkDirection};
for my $Object ( sort { lc $a cmp lc $b } keys %{$ObjectList} ) {
for my $Item ( @{ $ObjectList->{$Object} } ) {
$Table{CellData}[$Row][0]{Content} ||= '';
$Table{CellData}[$Row][1]{Content} = $Item->{Title} || '';
}
continue {
$Row++;
}
}
}
$Table{ColumnData}[0]{Width} = 80;
$Table{ColumnData}[1]{Width} = 431;
# get PDF object
my $PDFObject = $Kernel::OM->Get('Kernel::System::PDF');
# set new position
$PDFObject->PositionSet(
Move => 'relativ',
Y => -15,
);
# output headline
$PDFObject->Text(
Text => $LayoutObject->{LanguageObject}->Translate('Linked Objects'),
Height => 7,
Type => 'Cut',
Font => 'ProportionalBoldItalic',
FontSize => 7,
Color => '#666666',
);
# set new position
$PDFObject->PositionSet(
Move => 'relativ',
Y => -4,
);
# table params
$Table{Type} = 'Cut';
$Table{Border} = 0;
$Table{FontSize} = 6;
$Table{BackgroundColor} = '#DDDDDD';
$Table{Padding} = 1;
$Table{PaddingTop} = 3;
$Table{PaddingBottom} = 3;
# output table
$Self->_PDFOutputTable(
Table => \%Table,
);
return 1;
}
# output a table, accross several pages if neccessary
sub _PDFOutputTable {
my ( $Self, %Param ) = @_;
# check needed stuff
if ( !defined( $Param{Table} ) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Need Table!"
);
return;
}
# just for having shorter names
my $Table = $Param{Table};
my $Page = $Self->{Page};
PAGE:
for ( $Page->{PageCount} .. $Page->{MaxPages} ) {
# get PDF object
my $PDFObject = $Kernel::OM->Get('Kernel::System::PDF');
# output table (or a fragment of it)
%{$Table} = $PDFObject->Table( %{$Table} );
# stop output or output next page
if ( $Table->{State} ) {
last PAGE;
}
else {
$PDFObject->PageNew(
%{$Page},
FooterRight => join( ' ', $Page->{PageText}, $Page->{PageCount} ),
);
$Page->{PageCount}++;
}
}
$Self->{Page}->{PageCount} = $Page->{PageCount};
return 1;
}
1;