[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