[Bps-public-commit] rt-extension-customerservice branch master updated. 4066684c8d03d0ae390d8430051f5372a863a876
BPS Git Server
git at git.bestpractical.com
Mon Feb 28 04:09:07 UTC 2022
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "rt-extension-customerservice".
The branch, master has been updated
discards 2f954ef88aee0aa1b27e43146fbecd2419db5380 (commit)
via 4066684c8d03d0ae390d8430051f5372a863a876 (commit)
This update added new revisions after undoing existing revisions. That is
to say, the old revision is not a strict subset of the new revision. This
situation occurs when you --force push a change and generate a repository
containing something like this:
* -- * -- B -- O -- O -- O (2f954ef88aee0aa1b27e43146fbecd2419db5380)
\
N -- N -- N (4066684c8d03d0ae390d8430051f5372a863a876)
When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 4066684c8d03d0ae390d8430051f5372a863a876
Author: Brad Embree <brad at bestpractical.com>
Date: Tue Jan 25 14:42:31 2022 -0800
Add initial version
diff --git a/Changes b/Changes
new file mode 100644
index 0000000..d987d01
--- /dev/null
+++ b/Changes
@@ -0,0 +1,4 @@
+Revision history for RT-Extension-CustomerService
+
+0.01 2022-01-25
+ - Initial version
diff --git a/META.yml b/META.yml
new file mode 100644
index 0000000..87a7cc0
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,29 @@
+---
+abstract: 'RT-Extension-CustomerService Extension'
+author:
+ - 'Best Practical Solutions, LLC <modules at bestpractical.com>'
+build_requires:
+ ExtUtils::MakeMaker: 6.59
+configure_requires:
+ ExtUtils::MakeMaker: 6.59
+distribution_type: module
+dynamic_config: 1
+generated_by: 'Module::Install version 1.19'
+license: gpl_2
+meta-spec:
+ url: http://module-build.sourceforge.net/META-spec-v1.4.html
+ version: 1.4
+name: RT-Extension-CustomerService
+no_index:
+ directory:
+ - etc
+ - inc
+requires:
+ perl: 5.10.1
+resources:
+ license: http://opensource.org/licenses/gpl-license.php
+ repository: https://github.com/bestpractical/rt-extension-customerservice
+version: '0.01'
+x_module_install_rtx_version: '0.42'
+x_requires_rt: 4.4.0
+x_rt_too_new: 5.2.0
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 0000000..3194818
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,12 @@
+use lib '.';
+use inc::Module::Install;
+
+RTx 'RT-Extension-CustomerService';
+license 'gpl_2';
+repository 'https://github.com/bestpractical/rt-extension-customerservice';
+
+requires_rt '4.4.0';
+rt_too_new '5.2.0';
+
+sign;
+WriteAll;
diff --git a/README b/README
new file mode 100644
index 0000000..e271ede
--- /dev/null
+++ b/README
@@ -0,0 +1,207 @@
+NAME
+ RT-Extension-CustomerService - Default Customer Service configuration
+ for Request Tracker
+
+RT VERSION
+ Works with RT 4.4, 5.0
+
+INSTALLATION
+ perl Makefile.PL
+ make
+ make install
+ May need root permissions
+
+ Edit your /opt/rt5/etc/RT_SiteConfig.pm
+ Add this line:
+
+ Plugin('RT::Extension::CustomerService');
+
+ If you don't add the Plugin line and save, you will see errors in
+ the next step.
+
+ make initdb
+ Only run this the first time you install this module.
+
+ If you run this twice, you may end up with duplicate data in your
+ database.
+
+ If you are upgrading this module, check for upgrading instructions
+ in case changes need to be made to your database.
+
+ Clear your mason cache
+ rm -rf /opt/rt5/var/mason_data/obj
+
+ Restart your webserver
+
+DESCRIPTION
+ One common use for Request Tracker (RT) is tracking customer issues,
+ typically related to customer service. "Customer Service" is often a
+ department, either a designated department with many agents for large
+ organizations, or sometimes only one or two people who handle all
+ customer service for a smaller organization.
+
+ RT is used to track incoming customer service requests so they don't get
+ lost and can be assigned to individual people to handle. It's also
+ useful for gathering general reporting on the volume of customer service
+ requests and what types of problems seem to generate the most requests.
+
+ This extension provides an initialdata
+ <https://docs.bestpractical.com/rt/latest/initialdata.html/> file to
+ configure a queue with some sensible default rights configuration for a
+ typical customer service department. Once installed, you can then edit
+ the configuration to best suit your needs.
+
+ Service Queue
+ After installing, you'll see a new queue called Service for tracking all
+ of the incoming customer service requests. You can change the name to
+ anything you like after installing. In a typical configuration, you will
+ also want to assign an RT email address, like service at example.com or
+ cs at example.com to create tickets in this queue.
+
+ User Groups
+ Three new user groups are added to handle different aspects of customer
+ service: Service Representatives, Service Engineers, and Service
+ Managers.
+
+ Service Representatives are the front line of the customer service
+ department and will handle the requests as they come into the system.
+
+ Service Engineers assist the Service Representatives when an issue
+ requires a high level of product knowledge.
+
+ Service Managers review requests to ensure the customer needs are being
+ met and also to track how many requests are coming in, and what is
+ generating those requests.
+
+ Rights
+ Some typical initial rights are set on the Service queue. The system
+ group "Everyone" gets a default set of rights to allow customers to
+ create tickets. Everyone is a system group provided with RT, and as the
+ name implies it encompasses every user in RT.
+
+ <p>TODO: image for everyone group rights<img width="500px"
+ src="https://static.bestpractical.com/images/customerservice/everyone_gr
+ oup_rights.png" alt="Group rights for 'Everyone' group on 'Service'
+ queue" /></p>
+
+ These rights are usually the minimum needed for a typical customer
+ service department. Anyone is able to write into your customer service
+ address with a customer service request, and they can reply and
+ follow-up on that request if you send them some questions.
+
+ The extension also grants "ShowTicket" to the Requestor role. If your
+ end users have access to RT's self service interface, this allows them
+ to see only tickets where they are the Requestor, which should be the
+ tickets they opened.
+
+ Your staff users will need many more rights to work on tickets. To make
+ it easy to add and remove access for staff users, this extension creates
+ three new groups: Service Representatives, Service Engineers, and
+ Service Managers.
+
+ Rights are granted to these groups, so membership in the group is all a
+ user needs to get those rights.
+
+ Service Representatives and Service Engineers are granted a set of
+ rights to allow them to manage the incoming tickets and make changes to
+ the tickets as required.
+
+ <p>TODO: image for rep/eng group rights<img width="500px"
+ src="https://static.bestpractical.com/images/customerservice/rep_eng_gro
+ up_rights.png" alt="Group rights for 'Service Representatives' and
+ 'Service Engineers' groups on 'Service' queue" /></p>
+
+ Service Managers are granted a set of rights to view tickets but not
+ make any changes to the tickets.
+
+ <p>TODO: image for manager group rights<img width="500px"
+ src="https://static.bestpractical.com/images/customerservice/manager_gro
+ up_rights.png" alt="Group rights for 'Service Managers' group on
+ 'Service' queue" /></p>
+
+ Service Lifecycle
+ RT allows you to create and configure custom workflows for each queue in
+ the system. In RT a ticket workflow is known as a Lifecycle
+ <https://docs.bestpractical.com/rt/latest/customizing/lifecycles.html>.
+ This extension provies a custom lifecycle called "service" that defines
+ the various statuses a ticket can be in.
+
+ Custom Fields
+ RT allows you to define custom fields on tickets, which can be anything
+ you need to record and track. This extension provides some common to
+ customer service: Customer, Severity, Product, Serial Number, and
+ Warranty Required.
+
+ Customer is an autocomplete type field, which means users can type in
+ the box and if there is a defined value, it will autocomplete in a menu
+ below the field. If the user needs to add a value that hasn't been used
+ before, they can type in a completely new value. If you would prefer
+ this to be a dropdown like Severity, you can change this in the admin
+ section also.
+
+ Severity is a dropdown with typical High, Medium, Low values. As an RT
+ admin, you can change these values or add to them at Admin > Custom
+ Fields, then click on Severity.
+
+ Product is an autocomplete type field, which means users can type in the
+ box and if there is a defined value, it will autocomplete in a menu
+ below the field. If the user needs to add a value that hasn't been used
+ before, they can type in a completely new value. If you would prefer
+ this to be a dropdown like Severity, you can change this in the admin
+ section also.
+
+ Serial Number is a simple text field that will allow the user to type in
+ a single value.
+
+ Warranty Required is a dropdown with Yes or No values. As an RT admin,
+ you can change these values or add to them at Admin > Custom Fields,
+ then click on Warranty Required.
+
+ Service Dashboard
+ This extension creates a dashboard called "Service", accessible to any
+ member of the Service Representative group. This dashboard has a default
+ saved search called "Highest severity tickets waiting on customer
+ service".
+
+ As the name suggests, this saved search shows all tickets waiting for
+ customer service and displays them in order by severity, so the most
+ important will be at the top.
+
+ Next Steps
+ This extension provides a good starting point and you can start using it
+ right away. Here are some additional things you can do to customize your
+ configuration:
+
+ * Create new user accounts for other staff and add them to one of the
+ new Groups. You might also remove the root user if that user account
+ won't be involved in customer service.
+
+ * Update the custom fields by changing the values in the dropdowns or
+ adding other custom fields that better fit your system.
+
+ * Edit your templates to customize the default messages you send to
+ users. You can find templates at Admin > Global > Templates. For
+ example, the "Autoreply in HTML" is the default template that goes
+ to users when they open a ticket.
+
+ * Users working primarily in customer service can edit their
+ preferences and set Service as their default queue.
+
+ * Users can select Reports > Update this menu and add the Service
+ dashboard to their reports menu. The RT administrator can do this
+ for all users as well.
+
+AUTHOR
+ Best Practical Solutions, LLC <modules at bestpractical.com>
+
+ All bugs should be reported via email to
+ bug-RT-Extension-CustomerService at rt.cpan.org
+ or via the web at
+ http://rt.cpan.org/Public/Dist/Display.html?Name=RT-Extension-CustomerService
+LICENSE AND COPYRIGHT
+ This software is Copyright (c) 2022 by BPS
+
+ This is free software, licensed under:
+
+ The GNU General Public License, Version 2, June 1991
+
diff --git a/etc/Customerservice_Config.pm b/etc/Customerservice_Config.pm
new file mode 100644
index 0000000..596d057
--- /dev/null
+++ b/etc/Customerservice_Config.pm
@@ -0,0 +1,18 @@
+Set(
+ %CustomFieldGroupings,
+ 'RT::Ticket' => [
+ 'Service Information' => [ 'Customer', 'Order #', 'Product', 'Serial #', 'Severity', 'Warranty Required', 'Warranty Order #' ],
+ 'Supplier Information' => [ 'Supplier', 'Supplier PO #', 'Supplier Warranty Order #' ],
+ ],
+);
+Set(
+ %LinkedQueuePortlets,
+ (
+ 'Service' => [
+ { 'Supplier' => [ 'All' ] },
+ ],
+ 'Supplier' => [
+ { 'Service' => [ 'All' ] },
+ ],
+ )
+);
diff --git a/etc/Lifeycle_Config.pm b/etc/Lifeycle_Config.pm
new file mode 100644
index 0000000..297b287
--- /dev/null
+++ b/etc/Lifeycle_Config.pm
@@ -0,0 +1,105 @@
+Set(%Lifecycles,
+ service => {
+ initial => [qw(new)],
+ active => [qw(open stalled), 'waiting for service', 'waiting for customer', 'waiting for engineer', 'waiting for supplier'],
+ inactive => [qw(resolved rejected deleted)],
+ defaults => {
+ on_create => 'new',
+ approved => 'open',
+ denied => 'rejected',
+ reminder_on_open => 'open',
+ reminder_on_resolve => 'resolved',
+ },
+ transitions => {
+ "" => [qw(new open resolved)],
+ # from => [ to list ],
+ new => [qw( open stalled resolved rejected deleted), 'waiting for service', 'waiting for customer', 'waiting for engineer', 'waiting for supplier'],
+ open => [qw(new stalled resolved rejected deleted), 'waiting for service', 'waiting for customer', 'waiting for engineer', 'waiting for supplier'],
+ stalled => [qw(new open resolved rejected deleted), 'waiting for service', 'waiting for customer', 'waiting for engineer', 'waiting for supplier'],
+ resolved => [qw(new open stalled rejected deleted), 'waiting for service', 'waiting for customer', 'waiting for engineer', 'waiting for supplier'],
+ rejected => [qw(new open stalled resolved deleted), 'waiting for service', 'waiting for customer', 'waiting for engineer', 'waiting for supplier'],
+ deleted => [qw(new open)],
+ 'waiting for service' => [qw(open stalled resolved rejected deleted), 'waiting for customer', 'waiting for engineer', 'waiting for supplier'],
+ 'waiting for customer' => [qw(open stalled resolved rejected deleted), 'waiting for service', 'waiting for engineer', 'waiting for supplier'],
+ 'waiting for engineer' => [qw(open stalled resolved rejected deleted), 'waiting for customer', 'waiting for service', 'waiting for supplier'],
+ 'waiting for supplier' => [qw(open stalled resolved rejected deleted), 'waiting for customer', 'waiting for service', 'waiting for engineer'],
+ },
+ rights => {
+ '* -> deleted' => 'DeleteTicket',
+ '* -> *' => 'ModifyTicket',
+ },
+ actions => [
+ 'new -> open' => { label => 'Open It', update => 'Respond' },
+ 'new -> resolved' => { label => 'Resolve', update => 'Comment' },
+ 'new -> rejected' => { label => 'Reject', update => 'Respond' },
+ 'new -> deleted' => { label => 'Delete', },
+ 'open -> stalled' => { label => 'Stall', update => 'Comment' },
+ 'open -> resolved' => { label => 'Resolve', update => 'Comment' },
+ 'open -> rejected' => { label => 'Reject', update => 'Respond' },
+ 'stalled -> open' => { label => 'Open It', },
+ 'resolved -> open' => { label => 'Re-open', update => 'Comment' },
+ 'rejected -> open' => { label => 'Re-open', update => 'Comment' },
+ 'deleted -> open' => { label => 'Undelete', },
+ ],
+ },
+ supplier => {
+ initial => [qw(new)],
+ active => [qw(open stalled), 'waiting for service', 'waiting for supplier', 'waiting for engineer'],
+ inactive => [qw(resolved rejected deleted)],
+ defaults => {
+ on_create => 'new',
+ approved => 'open',
+ denied => 'rejected',
+ reminder_on_open => 'open',
+ reminder_on_resolve => 'resolved',
+ },
+ transitions => {
+ "" => [qw(new open resolved)],
+ # from => [ to list ],
+ new => [qw( open stalled resolved rejected deleted), 'waiting for service', 'waiting for supplier', 'waiting for engineer'],
+ open => [qw(new stalled resolved rejected deleted), 'waiting for service', 'waiting for supplier', 'waiting for engineer'],
+ stalled => [qw(new open resolved rejected deleted), 'waiting for service', 'waiting for supplier', 'waiting for engineer'],
+ resolved => [qw(new open stalled rejected deleted), 'waiting for service', 'waiting for supplier', 'waiting for engineer'],
+ rejected => [qw(new open stalled resolved deleted), 'waiting for service', 'waiting for supplier', 'waiting for engineer'],
+ deleted => [qw(new open stalled resolved rejected )],
+ 'waiting for service' => [qw(open stalled resolved rejected deleted), 'waiting for supplier', 'waiting for engineer'],
+ 'waiting for supplier' => [qw(open stalled resolved rejected deleted), 'waiting for service', 'waiting for engineer'],
+ 'waiting for engineer' => [qw(open stalled resolved rejected deleted), 'waiting for service', 'waiting for supplier'],
+ },
+ rights => {
+ '* -> deleted' => 'DeleteTicket',
+ '* -> *' => 'ModifyTicket',
+ },
+ actions => [
+ 'new -> open' => { label => 'Open It', update => 'Respond' },
+ 'new -> resolved' => { label => 'Resolve', update => 'Comment' },
+ 'new -> rejected' => { label => 'Reject', update => 'Respond' },
+ 'new -> deleted' => { label => 'Delete', },
+ 'open -> stalled' => { label => 'Stall', update => 'Comment' },
+ 'open -> resolved' => { label => 'Resolve', update => 'Comment' },
+ 'open -> rejected' => { label => 'Reject', update => 'Respond' },
+ 'stalled -> open' => { label => 'Open It', },
+ 'resolved -> open' => { label => 'Re-open', update => 'Comment' },
+ 'rejected -> open' => { label => 'Re-open', update => 'Comment' },
+ 'deleted -> open' => { label => 'Undelete', },
+ ],
+ },
+ __maps__ => {
+ 'service -> default' => {
+ 'waiting for customer' => 'stalled',
+ 'waiting for engineer' => 'stalled',
+ 'waiting for service' => 'stalled',
+ },
+ 'service -> supplier' => {
+ 'waiting for customer' => 'waiting for service',
+ },
+ 'supplier -> default' => {
+ 'waiting for engineer' => 'stalled',
+ 'waiting for service' => 'stalled',
+ 'waiting for supplier' => 'stalled',
+ },
+ 'supplier -> service' => {
+ 'waiting for supplier' => 'waiting for service',
+ },
+ }
+);
diff --git a/etc/initialdata b/etc/initialdata
new file mode 100644
index 0000000..39212fb
--- /dev/null
+++ b/etc/initialdata
@@ -0,0 +1,363 @@
+use strict;
+use warnings;
+
+our @Queues = (
+ {
+ Name => 'Service',
+ Description => 'Queue for triaging customer service tickets.',
+ Lifecycle => 'service',
+ },
+ {
+ Name => 'Supplier',
+ Description => 'Queue for tickets related to customer service tickets that need supplier support or warranty.',
+ Lifecycle => 'supplier',
+ },
+);
+
+our @Groups = (
+ {
+ Name => 'Service Engineers',
+ Description => 'Group for customer service engineers.',
+ },
+ {
+ Name => 'Service Managers',
+ Description => 'Group for customer service managers.',
+ },
+ {
+ Name => 'Service Representatives',
+ Description => 'Group for customer service representatives.',
+ },
+);
+
+my @EveryoneRights = qw/CreateTicket SeeQueue/;
+our @ACL = map {
+ {
+ Right => $_,
+ Queue => 'Service',
+ GroupDomain => 'SystemInternal',
+ GroupType => 'Everyone'
+ },
+} @EveryoneRights;
+
+my @RequestorRights = qw/ShowTicket ReplyToTicket/;
+push @ACL, map {
+ {
+ Right => $_,
+ Queue => 'Service',
+ GroupDomain => 'RT::Queue-Role',
+ GroupType => 'Requestor',
+ },
+ {
+ Right => $_,
+ Queue => 'Supplier',
+ GroupDomain => 'RT::Queue-Role',
+ GroupType => 'Requestor',
+ },
+} @RequestorRights;
+
+my @ServiceEngsAndRepsRights = qw/CommentOnTicket Watch SeeCustomField
+ SeeQueue ShowTicket OwnTicket WatchAsAdminCC StealTicket TakeTicket
+ ShowTicketComments ModifyTicket ModifyCustomField ShowOutgoingEmail
+ CreateTicket ReplyToTicket ReassignTicket
+/;
+push @ACL, map {
+ {
+ Right => $_,
+ Queue => 'Service',
+ GroupDomain => 'UserDefined',
+ GroupId => 'Service Engineers',
+ },
+ {
+ Right => $_,
+ Queue => 'Service',
+ GroupDomain => 'UserDefined',
+ GroupId => 'Service Representatives',
+ },
+ {
+ Right => $_,
+ Queue => 'Supplier',
+ GroupDomain => 'UserDefined',
+ GroupId => 'Service Engineers',
+ },
+ {
+ Right => $_,
+ Queue => 'Supplier',
+ GroupDomain => 'UserDefined',
+ GroupId => 'Service Representatives',
+ },
+} @ServiceEngsAndRepsRights;
+
+my @ServiceManagersRights = qw/SeeCustomField SeeQueue ShowTicket
+ WatchAsAdminCc ShowOutgoingEmail ShowTicketComments DeleteTicket
+/;
+push @ACL, map {
+ {
+ Right => $_,
+ Queue => 'Service',
+ GroupDomain => 'UserDefined',
+ GroupId => 'Service Managers',
+ },
+ {
+ Right => $_,
+ Queue => 'Supplier',
+ GroupDomain => 'UserDefined',
+ GroupId => 'Service Managers',
+ },
+} @ServiceManagersRights;
+
+our @CustomFields = (
+ {
+ Name => 'Customer',
+ Type => 'AutocompleteSingle',
+ LookupType => 'RT::Queue-RT::Ticket',
+ Description => 'The customer with the issue',
+ EntryHint => 'The customer with the issue',
+ ApplyTo => 'Service',
+ Values => [],
+ },
+ {
+ Name => 'Order #',
+ Type => 'FreeformSingle',
+ LookupType => 'RT::Queue-RT::Ticket',
+ Description => 'The customer order for the product with an issue',
+ EntryHint => 'The customer order for the product with an issue',
+ ApplyTo => 'Service',
+ Values => [],
+ },
+ {
+ Name => 'Product',
+ Type => 'AutocompleteSingle',
+ LookupType => 'RT::Queue-RT::Ticket',
+ Description => 'The product with an issue',
+ EntryHint => 'The product with an issue',
+ ApplyTo => ['Service','Supplier'],
+ Values => [],
+ },
+ {
+ Name => 'Serial #',
+ Type => 'FreeformSingle',
+ LookupType => 'RT::Queue-RT::Ticket',
+ Description => 'The serial number for the product with an issue',
+ EntryHint => 'The serial number for the product with an issue',
+ ApplyTo => ['Service','Supplier'],
+ Values => [],
+ },
+ {
+ Name => 'Severity',
+ Type => 'SelectSingle',
+ LookupType => 'RT::Queue-RT::Ticket',
+ Description => 'The severity of the issue',
+ EntryHint => 'The severity of the issue',
+ ApplyTo => 'Service',
+ RenderType => 'Dropdown',
+ Values => [
+ { Name => 'Low', SortOrder => 1 },
+ { Name => 'Medium', SortOrder => 2 },
+ { Name => 'High', SortOrder => 3 },
+ ],
+ },
+ {
+ Name => 'Supplier',
+ Type => 'AutocompleteSingle',
+ LookupType => 'RT::Queue-RT::Ticket',
+ Description => 'The supplier for the product with an issue',
+ EntryHint => 'The supplier for the product with an issue',
+ ApplyTo => ['Service','Supplier'],
+ Values => [],
+ },
+ {
+ Name => 'Supplier PO #',
+ Type => 'FreeformSingle',
+ LookupType => 'RT::Queue-RT::Ticket',
+ Description => 'The supplier purchase order that contained the product with an issue',
+ EntryHint => 'The supplier purchase order that contained the product with an issue',
+ ApplyTo => ['Service','Supplier'],
+ Values => [],
+ },
+ {
+ Name => 'Supplier Warranty Order #',
+ Type => 'FreeformSingle',
+ LookupType => 'RT::Queue-RT::Ticket',
+ Description => 'The supplier order with a replacement for the product with an issue',
+ EntryHint => 'The supplier order with a replacement for the product with an issue',
+ ApplyTo => 'Supplier',
+ Values => [],
+ },
+ {
+ Name => 'Warranty Required',
+ Type => 'SelectSingle',
+ LookupType => 'RT::Queue-RT::Ticket',
+ Description => 'Does the product issue qualify for warranty replacement of the product',
+ EntryHint => 'Does the product issue qualify for warranty replacement of the product',
+ ApplyTo => ['Service','Supplier'],
+ RenderType => 'List',
+ Values => [
+ { Name => 'No', SortOrder => 1 },
+ { Name => 'Yes', SortOrder => 2 },
+ ],
+ },
+ {
+ Name => 'Warranty Order #',
+ Type => 'FreeformSingle',
+ LookupType => 'RT::Queue-RT::Ticket',
+ Description => 'The order with a replacement for the product with an issue',
+ EntryHint => 'The order with a replacement for the product with an issue',
+ ApplyTo => 'Service',
+ Values => [],
+ },
+);
+
+our @Attributes = (
+ {
+ Name => 'SavedSearch',
+ Description => 'Highest severity tickets waiting on service',
+ Object => sub {
+ my $GroupName = 'Service Representatives';
+ my $group = RT::Group->new( RT->SystemUser );
+
+ my( $ret, $msg ) = $group->LoadUserDefinedGroup( $GroupName );
+ die $msg unless $ret;
+
+ return $group;
+ },
+ Content => {
+ Format => qq['<b><a href="__WebPath__/Ticket/Display.html?id=__id__">__id__</a></b>/TITLE:#',
+'<b><a href="__WebPath__/Ticket/Display.html?id=__id__">__Subject__</a></b>/TITLE:Subject',
+Status,
+'__CustomFieldView.{Severity}__',
+Priority,
+'__NEWLINE__',
+'__NBSP__',
+'<small>__Requestors__</small>',
+'<small>__CreatedRelative__</small>',
+Owner,
+'__CustomField.{Customer}__'],
+ Query => "( Queue = 'Service' OR Queue = 'Supplier' ) AND ( Status = 'waiting for service' OR Status = 'open' OR Status = 'new' )",
+ OrderBy => 'CustomFieldView.{Severity}',
+ Order => 'DESC'
+ },
+ },
+ {
+ Name => 'SavedSearch',
+ Description => 'Highest severity tickets waiting on engineer',
+ Object => sub {
+ my $GroupName = 'Service Engineers';
+ my $group = RT::Group->new( RT->SystemUser );
+
+ my( $ret, $msg ) = $group->LoadUserDefinedGroup( $GroupName );
+ die $msg unless $ret;
+
+ return $group;
+ },
+ Content => {
+ Format => qq['<b><a href="__WebPath__/Ticket/Display.html?id=__id__">__id__</a></b>/TITLE:#',
+'<b><a href="__WebPath__/Ticket/Display.html?id=__id__">__Subject__</a></b>/TITLE:Subject',
+Status,
+'__CustomFieldView.{Severity}__',
+Priority,
+'__NEWLINE__',
+'__NBSP__',
+'<small>__Requestors__</small>',
+'<small>__CreatedRelative__</small>',
+Owner,
+'__CustomField.{Customer}__'],
+ Query => "( Queue = 'Service' OR Queue = 'Supplier' ) AND Status = 'waiting for engineer'",
+ OrderBy => 'CustomFieldView.{Severity}',
+ Order => 'DESC'
+ },
+ },
+);
+
+our @Final = (
+ sub {
+ for my $new_group ( 'Service Engineers', 'Service Managers', 'Service Representatives' ) {
+ my $GroupName = $new_group;
+ my $group = RT::Group->new( RT->SystemUser );
+
+ my ( $ret, $msg ) = $group->LoadUserDefinedGroup( $GroupName );
+ die $msg unless $ret;
+
+ my $root = RT::User->new( RT->SystemUser );
+ $root->Load( 'root' );
+
+ ($ret, $msg) = $group->AddMember( $root->PrincipalObj->Id );
+ print "Could not load root user: $msg\n" unless $ret;
+
+ foreach my $right ( qw/SeeGroup SeeGroupDashboard ShowSavedSearches/ ) {
+ ($ret, $msg) = $group->PrincipalObj->GrantRight( Right => $right, Object => $group );
+ print "Failed to grant right $right: $msg\n" unless $ret;
+ }
+
+ # Create dashboard for service representative group
+ if ( $new_group eq 'Service Representatives' ) {
+ my $dashboard = RT::Dashboard->new( RT->SystemUser );
+ ($ret, $msg) = $dashboard->Save(
+ Name => 'Highest severity waiting for service',
+ Privacy => 'RT::Group-'.$group->Id,
+ );
+ die "Could not create dashboard! $msg\n" unless $ret;
+
+ my $saved_search = RT::Attribute->new( RT->SystemUser );
+ ($ret, $msg) = $saved_search->LoadByCols(
+ Name => 'SavedSearch',
+ Description => 'Highest severity tickets waiting on service',
+ ObjectType => 'RT::Group',
+ ObjectId => $group->Id,
+ );
+ die "Could not load highest severity saved search: $msg" unless $ret;
+
+ ($ret, $msg) = $dashboard->Update( Panes => {
+ body => [
+ {
+ description => "Saved Search: Highest severity tickets waiting on service",
+ id => $saved_search->Id,
+ pane => "body",
+ portlet_type => "search",
+ privacy => "RT::Group-".$group->Id
+ }
+ ],
+ sidebar => [
+ ]
+ });
+ die "$msg" unless $ret;
+ }
+ elsif ( $new_group eq 'Service Engineers' ) {
+ my $dashboard = RT::Dashboard->new( RT->SystemUser );
+ ($ret, $msg) = $dashboard->Save(
+ Name => 'Highest severity waiting for engineer',
+ Privacy => 'RT::Group-'.$group->Id,
+ );
+ die "Could not create dashboard! $msg\n" unless $ret;
+
+ my $saved_search = RT::Attribute->new( RT->SystemUser );
+ ($ret, $msg) = $saved_search->LoadByCols(
+ Name => 'SavedSearch',
+ Description => 'Highest severity tickets waiting on engineer',
+ ObjectType => 'RT::Group',
+ ObjectId => $group->Id,
+ );
+ die "Could not load highest severity engineer saved search: $msg" unless $ret;
+
+ ($ret, $msg) = $dashboard->Update( Panes => {
+ body => [
+ {
+ description => "Saved Search: Highest severity tickets waiting on engineer",
+ id => $saved_search->Id,
+ pane => "body",
+ portlet_type => "search",
+ privacy => "RT::Group-".$group->Id
+ }
+ ],
+ sidebar => [
+ ]
+ });
+ die "$msg" unless $ret;
+ }
+ }
+ },
+ sub {
+ my $cf = RT::CustomField->new( RT->SystemUser );
+ $cf->LoadByName( Name => 'Warranty Required' );
+ $cf->SetDefaultValues( Values => 'No' );
+ },
+);
diff --git a/lib/RT/Extension/CustomerService.pm b/lib/RT/Extension/CustomerService.pm
new file mode 100644
index 0000000..1960bd1
--- /dev/null
+++ b/lib/RT/Extension/CustomerService.pm
@@ -0,0 +1,319 @@
+use strict;
+use warnings;
+package RT::Extension::CustomerService;
+
+our $VERSION = '0.01';
+
+=head1 NAME
+
+RT-Extension-CustomerService - Default Customer Service configuration for Request Tracker
+
+=head1 RT VERSION
+
+Works with RT 4.4, 5.0
+
+=head1 INSTALLATION
+
+=over
+
+=item C<perl Makefile.PL>
+
+=item C<make>
+
+=item C<make install>
+
+May need root permissions
+
+=item Edit your F</opt/rt5/etc/RT_SiteConfig.pm>
+
+Add this line:
+
+ Plugin('RT::Extension::CustomerService');
+
+B<If you don't add the Plugin line and save, you will see errors in the next step.>
+
+=item C<make initdb>
+
+Only run this the first time you install this module.
+
+If you run this twice, you may end up with duplicate data in your database.
+
+If you are upgrading this module, check for upgrading instructions in case
+changes need to be made to your database.
+
+=item Clear your mason cache
+
+ rm -rf /opt/rt5/var/mason_data/obj
+
+=item Restart your webserver
+
+=back
+
+=head1 DESCRIPTION
+
+One common use for Request Tracker (RT) is tracking customer issues, typically
+related to customer service. "Customer Service" is often a department, either a
+designated department with many agents for large organizations, or sometimes
+only one or two people who handle all customer service for a smaller
+organization.
+
+RT is used to track incoming customer service requests so they don't get lost
+and can be assigned to individual people to handle. It's also useful for
+gathering general reporting on the volume of customer service requests and what
+types of problems seem to generate the most requests.
+
+This extension provides an L<initialdata|https://docs.bestpractical.com/rt/latest/initialdata.html/> file
+to configure a service queue and a supplier queue with some sensible default
+rights configured for a typical customer service department. Once installed, you
+can then edit the configuration to best suit your needs.
+
+=head2 Service Queue
+
+After installing, you'll see a new queue called L<Service> for tracking all of
+the incoming customer service requests. In a typical configuration, you will
+also want to assign an RT email address, like service at example.com or
+cs at example.com to create tickets in this queue.
+
+=head2 Supplier Queue
+
+After installing, you'll also see a second new queue called L<Supplier> for
+tracking all interactions with your different suppliers. If a customer service
+request requires you to contact a supplier you can open a dependent ticket in
+the supplier queue to track the interaction there. This keeps your
+communication with your supplier separate from your communication with your
+customer.
+
+=head2 User Groups
+
+Three new user groups are added to handle different aspects of customer service:
+Service Representatives, Service Engineers, and Service Managers.
+
+Service Representatives are the front line of the customer service department
+and will handle the requests as they come into the system. If they need
+assistance from someone with more detailed product knowledge they can set a
+ticket status to 'waiting on engineer' and wait for a response.
+
+Service Engineers assist the Service Representatives when an issue requires more
+detailed product knowledge. If a ticket has a status of 'waiting for engineer'
+they can assist and then set the ticket status back to 'waiting for service' to
+indicate they are finished working on it.
+
+Service Managers review requests to ensure the customer needs are being met and
+also to track how many requests are coming in, and what is generating those
+requests.
+
+=head2 Rights
+
+Some typical initial rights are set on the L<Service> queue. The system group
+"Everyone" gets a default set of rights to allow customers to create tickets.
+Everyone is a system group provided with RT, and as the name implies it
+encompasses every user in RT.
+
+<p>TODO: image for everyone group rights<img width="500px" src="https://static.bestpractical.com/images/customerservice/everyone_group_rights.png"
+alt="Group rights for 'Everyone' group on 'Service' queue" /></p>
+
+These rights are usually the minimum needed for a typical customer service
+department. Anyone is able to write into your customer service address with a
+customer service request, and they can reply and follow-up on that request if
+you send them some questions.
+
+The extension also grants "ShowTicket" to the Requestor role. If your end users
+have access to RT's self service interface, this allows them to see only tickets
+where they are the Requestor, which should be the tickets they opened.
+
+Your staff users will need many more rights to work on tickets. To make it easy
+to add and remove access for staff users, this extension creates three new
+groups: Service Representatives, Service Engineers, and Service Managers.
+
+Rights are granted to these groups, so membership in the group is all a user
+needs to get those rights.
+
+Service Representatives and Service Engineers are granted a set of rights to
+allow them to manage the incoming tickets and make changes to the tickets as
+required.
+
+<p>TODO: image for rep/eng group rights<img width="500px" src="https://static.bestpractical.com/images/customerservice/rep_eng_group_rights.png"
+alt="Group rights for 'Service Representatives' and 'Service Engineers' groups on 'Service' queue" /></p>
+
+Service Managers are granted a set of rights to view tickets but not make any
+changes to the tickets.
+
+<p>TODO: image for manager group rights<img width="500px" src="https://static.bestpractical.com/images/customerservice/manager_group_rights.png"
+alt="Group rights for 'Service Managers' group on 'Service' queue" /></p>
+
+=head2 Lifecycles
+
+RT allows you to create and configure custom workflows for each queue in the
+system. In RT a ticket workflow is known as a L<Lifecycle|https://docs.bestpractical.com/rt/latest/customizing/lifecycles.html>.
+
+This extension provides two custom lifecycles called "service" and "supplier"
+that defines the various statuses a ticket can be in for the service and
+supplier queues.
+
+=begin HTML
+
+<p>TODO: image for service lifecycle<img width="500px" src="https://static.bestpractical.com/images/customerservice/service_lifecycle.png"
+alt="Lifecycle for 'Service' queue" /></p>
+
+<p>TODO: image for supplier lifecycle<img width="500px" src="https://static.bestpractical.com/images/customerservice/supplier_lifecycle.png"
+alt="Lifecycle for 'Supplier' queue" /></p>
+
+=end HTML
+
+=head2 Custom Fields
+
+RT allows you to define custom fields on tickets, which can be anything you need
+to record and track. This extension provides some common to customer service.
+
+Tickets in the Service queue have the following custom fields:
+
+=over
+
+=item * Customer
+
+The customer with the issue
+
+=item * Order #
+
+The order # for the product purchase
+
+=item * Product
+
+The product with the issue
+
+=item * Serial #
+
+The serial # of the product
+
+=item * Severity
+
+How serious is the issue?
+
+=item * Supplier
+
+Supplier of the product
+
+=item * Supplier PO #
+
+Your PO # with the supplier that contained the product with the issue. Useful
+for tracking if a specific purchase had multiple faulty products.
+
+=item * Warranty Required
+
+Does the issue qualify for a warranty replacement
+
+=item * Warranty Order #
+
+If there is a warranty the order for the replacement
+
+=back
+
+Tickets in the Supplier queue have some of the same custom fields as well as:
+
+=over
+
+=item * Supplier Warranty Order #
+
+If your supplier replaces the product under warranty the supplier order for the
+replacement
+
+=back
+
+Customer, Product, and Supplier are autocomplete fields. This means users can
+type in the box and if there is a matching value in the list of defined values
+for the field, it will autocomplete in a menu below the field. If the user needs
+to enter a value that is not one of the defined values, they can enter a
+completely new value.
+
+Severity is a dropdown with typical High, Medium, Low values. As an RT admin,
+you can change these values or add to them at Admin > Custom Fields, then click
+on Severity.
+
+Order #, Serial #, Supplier PO #, Warranty Order #, and Supplier Warranty Order #
+are simple text fields that will allow the user to type in a single value.
+
+Warranty Required is a dropdown with Yes or No values.
+
+As an RT admin, you can change the list of defined values for a custom field or
+change an autocomplete field to a dropdown field at Admin -> Custom Fields, then
+click on the custom field you would like to edit.
+
+=head2 Service Representative Dashboard
+
+This extension creates a dashboard called "Highest severity waiting for service",
+accessible to any member of the Service Representative group. This dashboard has
+a default saved search called "Highest severity tickets waiting on service".
+
+As the name suggests, this saved search shows all tickets waiting for customer
+service and displays them in order by severity, so the most important will be at
+the top.
+
+=head2 Service Engineer Dashboard
+
+This extension creates a dashboard called "Highest severity waiting for engineer",
+accessible to any member of the Service Engineer group. This dashboard has a
+default saved search called "Highest severity tickets waiting on engineer".
+
+As the name suggests, this saved search shows all tickets waiting for customer
+service engineers (status of 'waiting for engineer') and displays them in order
+by severity, so the most important will be at the top.
+
+=head2 Next Steps
+
+This extension provides a good starting point and you can start using it right
+away. Here are some additional things you can do to customize your
+configuration:
+
+=over
+
+=item *
+
+Create new user accounts for other staff and add them to one of the new Groups.
+You might also remove the root user if that user account won't be involved in
+customer service.
+
+=item *
+
+Update the custom fields by changing the values in the dropdowns or adding other
+custom fields that better fit your system.
+
+=item *
+
+Edit your templates to customize the default messages you send to users. You can
+find templates at Admin > Global > Templates. For example, the
+"Autoreply in HTML" is the default template that goes to users when they open a
+ticket.
+
+=item *
+
+The RT administrator can add one of the new dashboards to user Reports menu by
+going to Admin -> Users -> Select -> Modify Reports menu.
+
+=back
+
+=head1 AUTHOR
+
+Best Practical Solutions, LLC E<lt>modules at bestpractical.comE<gt>
+
+=for html <p>All bugs should be reported via email to <a
+href="mailto:bug-RT-Extension-CustomerService at rt.cpan.org">bug-RT-Extension-CustomerService at rt.cpan.org</a>
+or via the web at <a
+href="http://rt.cpan.org/Public/Dist/Display.html?Name=RT-Extension-CustomerService">rt.cpan.org</a>.</p>
+
+=for text
+ All bugs should be reported via email to
+ bug-RT-Extension-CustomerService at rt.cpan.org
+ or via the web at
+ http://rt.cpan.org/Public/Dist/Display.html?Name=RT-Extension-CustomerService
+
+=head1 LICENSE AND COPYRIGHT
+
+This software is Copyright (c) 2022 by BPS
+
+This is free software, licensed under:
+
+ The GNU General Public License, Version 2, June 1991
+
+=cut
+
+1;
-----------------------------------------------------------------------
Summary of changes:
etc/Customerservice_Config.pm | 21 ++-
etc/Lifeycle_Config.pm | 108 +++++++++----
etc/initialdata | 294 +++++++++++++++++++++++++++---------
lib/RT/Extension/CustomerService.pm | 155 +++++++++++++------
4 files changed, 431 insertions(+), 147 deletions(-)
hooks/post-receive
--
rt-extension-customerservice
More information about the Bps-public-commit
mailing list