При внедрении в компании системы обработки заявок с открытым исходным кодом OTRS мы столкнулись с тем, что представленные в системе варианты номеров тикетов не соответствуют пожеланиям.
Доступные варианты:
- AutoIncrement - увеличивает номер на 1 (формат - SystemID.counter(например, 1010138, 1010139)
- Date - использует текущую дату, SystemID и счетчик (вид: Year.Month.Day.SystemID.counter (200206231010138, 200206231010139)
- DateChecksum - счетчик дополняется контрольной суммой к строке из даты и SystemID. Формат строится как Year.Month.Day.SystemID.Counter.CheckSum. Контрольная сумма обновляется ежедневно (вид: 2002070110101520, 2002070110101535)
- Random - генерирует случайный номер в формате "SystemID.Random" (напр. 100057866352, 103745394596)
После бурного обсуждения, наконец придя к консенсусу, было решено, что Ticket ID должен:
- Иметь в себе дату, для удобного поиска заявок за конкретную дату и оценивания времени существования заявки.
- Содержать шестизначный увеличивающийся номер, не обнуляемый каждый день, как в варианте Date выше, для обратной совместимости с текущей системой учёта заявок.
То есть, что-то в духе: [taskid:170824.475020]
Выхода не было - пришлось писать свой модуль, благо код открытый.
Модули, предоставляющие форматы Ticket ID находятся в otrs/Kernel/System/Ticket/Number/
За основу был взят вариант Date
:
# --
# Copyright (C) 2001-2017 OTRS AG, http://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (AGPL). If you
# did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
# --
#
# Generates ticket numbers like yyyymmddss.... (e. g. 200206231010138)
# --
package Kernel::System::Ticket::Number::Date;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Log',
'Kernel::System::Main',
'Kernel::System::Time',
);
sub TicketCreateNumber {
my ( $Self, $JumpCounter ) = @_;
if ( !$JumpCounter ) {
$JumpCounter = 0;
}
# get needed objects
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
my $TimeObject = $Kernel::OM->Get('Kernel::System::Time');
# get needed config options
my $CounterLog = $ConfigObject->Get('Ticket::CounterLog');
my $SystemID = $ConfigObject->Get('SystemID');
# get current time
my ( $Sec, $Min, $Hour, $Day, $Month, $Year ) = $TimeObject->SystemTime2Date(
SystemTime => $TimeObject->SystemTime(),
);
# read count
my $Count = 0;
if ( -f $CounterLog ) {
my $ContentSCALARRef = $MainObject->FileRead(
Location => $CounterLog,
);
if ( $ContentSCALARRef && ${$ContentSCALARRef} ) {
($Count) = split( /;/, ${$ContentSCALARRef} );
# just debug
if ( $Self->{Debug} > 0 ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'debug',
Message => "Read counter from $CounterLog: $Count",
);
}
}
}
# count auto increment ($Count++)
$Count++;
$Count = $Count + $JumpCounter;
# write new count
my $Write = $MainObject->FileWrite(
Location => $CounterLog,
Content => \$Count,
);
if ($Write) {
if ( $Self->{Debug} > 0 ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'debug',
Message => "Write counter: $Count",
);
}
}
if ( $ConfigObject->Get('Ticket::NumberGenerator::Date::UseFormattedCounter') ) {
my $MinSize = $ConfigObject->Get('Ticket::NumberGenerator::MinCounterSize')
|| 5;
$Count = sprintf "%.*u", $MinSize, $Count;
}
# create new ticket number
my $Tn = $Year . $Month . $Day . $SystemID . $Count;
# Check ticket number. If exists generate new one!
if ( $Self->TicketIDLookup( TicketNumber => $Tn ) ) {
$Self->{LoopProtectionCounter}++;
if ( $Self->{LoopProtectionCounter} >= 16000 ) {
# loop protection
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "CounterLoopProtection is now $Self->{LoopProtectionCounter}!"
. " Stopped TicketCreateNumber()!",
);
return;
}
# create new ticket number again
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'notice',
Message => "Tn ($Tn) exists! Creating a new one.",
);
$Tn = $Self->TicketCreateNumber( $Self->{LoopProtectionCounter} );
}
return $Tn;
}
sub GetTNByString {
my ( $Self, $String ) = @_;
if ( !$String ) {
return;
}
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# get needed config options
my $CheckSystemID = $ConfigObject->Get('Ticket::NumberGenerator::CheckSystemID');
my $SystemID = '';
if ($CheckSystemID) {
$SystemID = $ConfigObject->Get('SystemID');
}
my $TicketHook = $ConfigObject->Get('Ticket::Hook');
my $TicketHookDivider = $ConfigObject->Get('Ticket::HookDivider');
# check current setting
if ( $String =~ /\Q$TicketHook$TicketHookDivider\E(\d{8}$SystemID\d{1,40})/i ) {
return $1;
}
# check default setting
if ( $String =~ /\Q$TicketHook\E:\s{0,2}(\d{8}$SystemID\d{1,40})/i ) {
return $1;
}
return;
}
1;
И соединён с AutoIncrement.pm
:
# --
# Copyright (C) 2001-2017 OTRS AG, http://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (AGPL). If you
# did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
# --
#
# Generates auto increment ticket numbers like ss.... (e. g. 1010138, 1010139, ...)
# --
package Kernel::System::Ticket::Number::AutoIncrement;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Log',
'Kernel::System::Main',
);
sub TicketCreateNumber {
my ( $Self, $JumpCounter ) = @_;
if ( !$JumpCounter ) {
$JumpCounter = 0;
}
# get needed objects
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
# get needed config options
my $CounterLog = $ConfigObject->Get('Ticket::CounterLog');
my $SystemID = $ConfigObject->Get('SystemID');
my $MinSize = $ConfigObject->Get('Ticket::NumberGenerator::AutoIncrement::MinCounterSize')
|| $ConfigObject->Get('Ticket::NumberGenerator::MinCounterSize')
|| 5;
# read count
my $Count = 0;
if ( -f $CounterLog ) {
my $ContentSCALARRef = $MainObject->FileRead(
Location => $CounterLog,
);
if ( $ContentSCALARRef && ${$ContentSCALARRef} ) {
($Count) = split /;/, ${$ContentSCALARRef};
if ( $Self->{Debug} > 0 ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'debug',
Message => "Read counter from $CounterLog: $Count",
);
}
}
}
# count auto increment ($Count++)
$Count++;
$Count = $Count + $JumpCounter;
# write new count
my $Write = $MainObject->FileWrite(
Location => $CounterLog,
Content => \$Count,
);
if ($Write) {
if ( $Self->{Debug} > 0 ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'debug',
Message => "Write counter: $Count",
);
}
}
# pad ticket number with leading '0' to length $MinSize (config option)
$Count = sprintf "%.*u", $MinSize, $Count;
# create new ticket number
my $Tn = $SystemID . $Count;
# Check ticket number. If exists generate new one!
if ( $Self->TicketIDLookup( TicketNumber => $Tn ) ) {
$Self->{LoopProtectionCounter}++;
if ( $Self->{LoopProtectionCounter} >= 16000 ) {
# loop protection
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "CounterLoopProtection is now $Self->{LoopProtectionCounter}!"
. " Stopped TicketCreateNumber()!",
);
return;
}
# create new ticket number again
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'notice',
Message => "Tn ($Tn) exists! Creating a new one.",
);
$Tn = $Self->TicketCreateNumber( $Self->{LoopProtectionCounter} );
}
return $Tn;
}
sub GetTNByString {
my ( $Self, $String ) = @_;
if ( !$String ) {
return;
}
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# get needed config options
my $CheckSystemID = $ConfigObject->Get('Ticket::NumberGenerator::CheckSystemID');
my $SystemID = '';
if ($CheckSystemID) {
$SystemID = $ConfigObject->Get('SystemID');
}
my $TicketHook = $ConfigObject->Get('Ticket::Hook');
my $TicketHookDivider = $ConfigObject->Get('Ticket::HookDivider');
my $MinSize = $ConfigObject->Get('Ticket::NumberGenerator::AutoIncrement::MinCounterSize')
|| $ConfigObject->Get('Ticket::NumberGenerator::MinCounterSize')
|| 5;
my $MaxSize = $MinSize + 5;
# check ticket number
if ( $String =~ /\Q$TicketHook$TicketHookDivider\E($SystemID\d{$MinSize,$MaxSize})/i ) {
return $1;
}
if ( $String =~ /\Q$TicketHook\E:\s{0,2}($SystemID\d{$MinSize,$MaxSize})/i ) {
return $1;
}
return;
}
1;
Вот, что из этого вышло:
--- Kernel/System/Ticket/Number/Date.pm 2017-05-31 22:10:16.000000000 +0300
+++ Kernel/System/Ticket/Number/DateAndNumber.pm 2017-08-24 19:50:08.622901900 +0300
@@ -7,10 +7,10 @@
# --
#
-# Generates ticket numbers like yyyymmddss.... (e. g. 200206231010138)
+# Generates ticket numbers like yymmddss.ID###### (e. g. 170823.654321)
# --
-package Kernel::System::Ticket::Number::Date;
+package Kernel::System::Ticket::Number::DateAndNumber;
use strict;
use warnings;
@@ -37,11 +37,16 @@
# get needed config options
my $CounterLog = $ConfigObject->Get('Ticket::CounterLog');
my $SystemID = $ConfigObject->Get('SystemID');
+ my $CheckSystemID = $ConfigObject->Get('Ticket::NumberGenerator::CheckSystemID');
+ my $MinSize = $ConfigObject->Get('Ticket::NumberGenerator::AutoIncrement::MinCounterSize')
+ || $ConfigObject->Get('Ticket::NumberGenerator::MinCounterSize')
+ || 5;
# get current time
my ( $Sec, $Min, $Hour, $Day, $Month, $Year ) = $TimeObject->SystemTime2Date(
SystemTime => $TimeObject->SystemTime(),
);
+ $Year = $Year % 100;
# read count
my $Count = 0;
@@ -92,7 +97,12 @@
}
# create new ticket number
- my $Tn = $Year . $Month . $Day . $SystemID . $Count;
+ my $Tn = '';
+ if ($CheckSystemID) {
+ $Tn = $Year . $Month . $Day . '.' . $SystemID . $Count;
+ } else {
+ $Tn = $Year . $Month . $Day . '.' . $Count;
+ }
# Check ticket number. If exists generate new one!
if ( $Self->TicketIDLookup( TicketNumber => $Tn ) ) {
@@ -144,12 +154,12 @@
my $TicketHookDivider = $ConfigObject->Get('Ticket::HookDivider');
# check current setting
- if ( $String =~ /\Q$TicketHook$TicketHookDivider\E(\d{8}$SystemID\d{1,40})/i ) {
+ if ( $String =~ /\Q$TicketHook$TicketHookDivider\E(\d{6}\.$SystemID\d{1,40})/i ) {
return $1;
}
# check default setting
- if ( $String =~ /\Q$TicketHook\E:\s{0,2}(\d{8}$SystemID\d{1,40})/i ) {
+ if ( $String =~ /\Q$TicketHook\E:\s{0,2}(\d{6}\.$SystemID\d{1,40})/i ) {
return $1;
}
Для выбора используемого формата, необходимо в настройках OTRS в секции Core::Ticket
изменить настройку Ticket::NumberGenerator
на только что созданную (она автоматически появится в списке после добавления файла в указанную директорию).
Полный файл:
# --
# Copyright (C) 2001-2017 OTRS AG, http://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (AGPL). If you
# did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
# --
#
# Generates ticket numbers like yymmddss.ID###### (e. g. 170823.654321)
# --
package Kernel::System::Ticket::Number::DateAndNumber;
use strict;
use warnings;
our @ObjectDependencies = (
'Kernel::Config',
'Kernel::System::Log',
'Kernel::System::Main',
'Kernel::System::Time',
);
sub TicketCreateNumber {
my ( $Self, $JumpCounter ) = @_;
if ( !$JumpCounter ) {
$JumpCounter = 0;
}
# get needed objects
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
my $MainObject = $Kernel::OM->Get('Kernel::System::Main');
my $TimeObject = $Kernel::OM->Get('Kernel::System::Time');
# get needed config options
my $CounterLog = $ConfigObject->Get('Ticket::CounterLog');
my $SystemID = $ConfigObject->Get('SystemID');
my $CheckSystemID = $ConfigObject->Get('Ticket::NumberGenerator::CheckSystemID');
my $MinSize = $ConfigObject->Get('Ticket::NumberGenerator::AutoIncrement::MinCounterSize')
|| $ConfigObject->Get('Ticket::NumberGenerator::MinCounterSize')
|| 5;
# get current time
my ( $Sec, $Min, $Hour, $Day, $Month, $Year ) = $TimeObject->SystemTime2Date(
SystemTime => $TimeObject->SystemTime(),
);
$Year = $Year % 100;
# read count
my $Count = 0;
if ( -f $CounterLog ) {
my $ContentSCALARRef = $MainObject->FileRead(
Location => $CounterLog,
);
if ( $ContentSCALARRef && ${$ContentSCALARRef} ) {
($Count) = split( /;/, ${$ContentSCALARRef} );
# just debug
if ( $Self->{Debug} > 0 ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'debug',
Message => "Read counter from $CounterLog: $Count",
);
}
}
}
# count auto increment ($Count++)
$Count++;
$Count = $Count + $JumpCounter;
# write new count
my $Write = $MainObject->FileWrite(
Location => $CounterLog,
Content => \$Count,
);
if ($Write) {
if ( $Self->{Debug} > 0 ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'debug',
Message => "Write counter: $Count",
);
}
}
if ( $ConfigObject->Get('Ticket::NumberGenerator::Date::UseFormattedCounter') ) {
my $MinSize = $ConfigObject->Get('Ticket::NumberGenerator::MinCounterSize')
|| 5;
$Count = sprintf "%.*u", $MinSize, $Count;
}
# create new ticket number
my $Tn = '';
if ($CheckSystemID) {
$Tn = $Year . $Month . $Day . '.' . $SystemID . $Count;
} else {
$Tn = $Year . $Month . $Day . '.' . $Count;
}
# Check ticket number. If exists generate new one!
if ( $Self->TicketIDLookup( TicketNumber => $Tn ) ) {
$Self->{LoopProtectionCounter}++;
if ( $Self->{LoopProtectionCounter} >= 16000 ) {
# loop protection
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "CounterLoopProtection is now $Self->{LoopProtectionCounter}!"
. " Stopped TicketCreateNumber()!",
);
return;
}
# create new ticket number again
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'notice',
Message => "Tn ($Tn) exists! Creating a new one.",
);
$Tn = $Self->TicketCreateNumber( $Self->{LoopProtectionCounter} );
}
return $Tn;
}
sub GetTNByString {
my ( $Self, $String ) = @_;
if ( !$String ) {
return;
}
# get config object
my $ConfigObject = $Kernel::OM->Get('Kernel::Config');
# get needed config options
my $CheckSystemID = $ConfigObject->Get('Ticket::NumberGenerator::CheckSystemID');
my $SystemID = '';
if ($CheckSystemID) {
$SystemID = $ConfigObject->Get('SystemID');
}
my $TicketHook = $ConfigObject->Get('Ticket::Hook');
my $TicketHookDivider = $ConfigObject->Get('Ticket::HookDivider');
# check current setting
if ( $String =~ /\Q$TicketHook$TicketHookDivider\E(\d{6}\.$SystemID\d{1,40})/i ) {
return $1;
}
# check default setting
if ( $String =~ /\Q$TicketHook\E:\s{0,2}(\d{6}\.$SystemID\d{1,40})/i ) {
return $1;
}
return;
}
1;