1=head1 Ticket Lifecycles
2
3By default, RT comes with ticket statuses that work for many types
4of workflows: new, open, stalled, resolved, rejected, and deleted.
5But there can be any number of workflows where these status values
6don't completely fit. RT allows you to add new custom status values and
7define their behavior with a feature called Lifecycles.
8
9=head1 Using the lifecycle web editor
10
11As an RT admin you can go to Admin->Lifecycles to edit and create lifecycles via the
12web interface.
13
14The web interface allows for easy visualization and editing of lifecycles.
15
16=head2 Editing statuses, transitions and categories
17
18Instructions for interacting with the web UI lifecycle builder can be found on the main "modify" page.
19The modify page allows for create and deleting statuses, adding transistions between statuses with
20drag and drop arrows and changing the categories of a status (initial, active and inactive).
21
22=for html <img alt="Lifecycle UI Modify" src="../images/lifecycle-ui-modify.png">
23
24=for :text [Lifecycle UI Modify F<docs/images/lifecycle-ui-modify.png>]
25
26=for :man [Lifecycle UI Modify F<docs/images/lifecycle-ui-modify.png>]
27
28=head2 Actions
29
30The actions tab allows for the easy editing, creating and deleting of actions. Actions are the options
31in the "Actions" drop down on ticket pages.
32
33=for html <img alt="Lifecycle UI Actions" src="../images/lifecycle-ui-actions.png">
34
35=for :text [Lifecycle UI Actions F<docs/images/lifecycle-ui-actions.png>]
36
37=for :man [Lifecycle UI Actions F<docs/images/lifecycle-ui-actions.png>]
38
39=head2 Rights
40
41Create, edit and delete custom rights for status transitions using the Rights tab.
42
43=for html <img alt="Lifecycle UI Rights" src="../images/lifecycle-ui-rights.png">
44
45=for :text [Lifecycle UI Rights F<docs/images/lifecycle-ui-rights.png>]
46
47=for :man [Lifecycle UI Rights F<docs/images/lifecycle-ui-rights.png>]
48
49=head2 Mappings
50
51The mappings tab allows for the mapping between the current lifecycle and the other lifecycles to be
52defined. This is needed when moving tickets between queues with different lifecycles.
53
54=for html <img alt="Lifecycle UI Mappings" src="../images/lifecycle-ui-mappings.png">
55
56=for :text [Lifecycle UI Mappings F<docs/images/lifecycle-ui-mappings.png>]
57
58=for :man [Lifecycle UI Mappings F<docs/images/lifecycle-ui-mappings.png>]
59
60=head2 Advanced
61
62Edit the configuration as JSON. This allows for more advanced lifecycle features that are available via
63setting the config on file to be utilized.
64
65=for html <img alt="Lifecycle UI Advanced" src="../images/lifecycle-ui-advanced.png">
66
67=for :text [Lifecycle UI Advanced F<docs/images/lifecycle-ui-advanced.png>]
68
69=for :man [Lifecycle UI Advanced F<docs/images/lifecycle-ui-advanced.png>]
70
71=head1 Using the site config file
72
73=head2 Adding a New Status
74
75Because Statuses are controlled via lifecycles, you must manipulate the entire
76lifecycle configuration to add a status. In earlier versions of RT new statuses
77could be added by adding a new element to an array in RT's config file. But
78because lifecyles are built around statuses, the entire lifecycle configuration
79must be modified even if you only need new statuses.
80
81=head3 Copy Lifecycle Config
82
83First, copy the C<%Lifecycles> hash from C<RT_Config.pm> and paste it into
84C<RT_SiteConfig.pm>.
85
86=head3 Add Status Value
87
88Add the status to the set where your new status belongs. This example adds
89C<approved> to the active statuses:
90
91    active => [ 'open', 'approved', 'stalled' ],
92
93=head3 Update Transitions
94
95Now the transitions section must be updated so that the new status can
96transition to the existing statuses and also so the existing statuses can
97transition to the new status.
98
99    new      => [qw(    open approved stalled resolved rejected deleted)],
100    open     => [qw(new      approved stalled resolved rejected deleted)],
101    approved => [qw(new open          stalled resolved rejected deleted)],
102    stalled  => [qw(new open approved         rejected resolved deleted)],
103    resolved => [qw(new open approved stalled          rejected deleted)],
104    rejected => [qw(new open approved stalled resolved          deleted)],
105    deleted  => [qw(new open approved stalled rejected resolved        )],
106
107=head2 Order Processing Example
108
109This guide demonstrates lifecycles using an order fulfillment
110system as a real-world example. You can find full lifecycles
111documentation in L<RT_Config/Lifecycles>.
112
113As with all RT custom configuration, if you are customizing the RT
114lifecycle, make your changes in your C<RT_SiteConfig.pm> file, not
115directly in C<RT_Config.pm>. If you are adding a new lifecycle, you can
116add a new entry with:
117
118    Set(%Lifecycles, my_new_lifecycle => { ... } );
119
120The detailed configuration options are discussed below. Once you add it
121and restart the server, the new lifecycle will be available on the
122queue configuration page.
123
124To show how you might use custom lifecycles, we're going to configure
125an RT lifecycle to process orders of some sort. In our order example,
126each ticket in the queue is considered a separate order and the orders
127have the following statuses:
128
129=over
130
131=item pending
132
133The order just came in untouched, pending purchase validation
134
135=item processing
136
137The order is being looked at for transaction processing
138
139=item delivery
140
141The order is out for delivery
142
143=item delivered
144
145The order was successfully delivered to its destination
146
147=item refunded
148
149The order was delivered but subsequently refunded
150
151=item declined
152
153There was an error in the process validation and the order was denied purchase
154
155=back
156
157In this particular example, the only status an order can start with is
158'pending.'  When a process coordinator chooses to take this order, it
159goes into processing. The order can then either be delivered or denied
160processing. Once denied, the lifecycle for that order ends. If it is
161delivered, the order can still be refunded.
162
163The following sections walk through each part of the configuration.
164You can find the full configuration at the end in case you want to
165see the exact syntax or use it to experiment with.
166
167=head3 Defining Status Values
168
169Every queue has a lifecycle assigned to it. Without changing any
170configuration, you are given two lifecycles to choose from: "default"
171and "approvals." The approvals lifecycle is used by the internal
172approvals queue, and should not be changed or used by other queues. Do
173not modify the approvals lifecycle unless you fully understand how RT
174approvals work.
175
176=for html <img alt="Lifecycle choices" src="../images/lifecycle-choices.png">
177
178=for :text [Lifecycle choices F<docs/images/lifecycle-choices.png>]
179
180=for :man [Lifecycle choices F<docs/images/lifecycle-choices.png>]
181
182In RT 4.0, the C<@ActiveStatus> and C<@InactiveStatus> configurations
183which were previously available are gone. The logic defined by those
184options is now a subset of RT's lifecycle features, as described here.
185
186A ticket naturally has three states: initial (I<new>), active (I<open> and
187I<stalled>), and inactive (I<resolved>, I<rejected>, and I<deleted>). These
188default settings look like this in the C<RT_Config.pm> file:
189
190    default => {
191        initial         => [ 'new' ],
192        active          => [ 'open', 'stalled' ],
193        inactive        => [ 'resolved', 'rejected', 'deleted' ],
194
195The initial state is the default starting place for new tickets, although
196you can create tickets with other statuses. Initial is generally used
197to acknowledge that a request has been made, but not yet acted on. RT
198sets the Started date on a ticket when it is moved out of the initial state.
199
200Active tickets are currently being worked on, inactive tickets have reached
201some final state. By default, inactive tickets don't show up in search
202results. The AutoOpen action sets a ticket's status to the first active
203status. You can find more details in L<RT_Config/"Lifecycle definitions">.
204
205Now we want to set up some statuses appropriate for order fulfillment,
206so we create a new top-level key called C<orders> and add our new status
207values.
208
209    Set( %Lifecycles, orders => {
210             initial  => [ 'pending' ],
211             active   => [ 'processing', 'delivery' ],
212             inactive => [ 'delivered', 'returned', 'declined', 'deleted' ],
213             # ...,
214    });
215
216We still use the initial, active and inactive categories, but we are
217able to define status values that are appropriate for the workflow
218we want to create. This should make the system more intuitive for users.
219
220=head3 Transitions
221
222The typical lifecycle follows the path initial -> active -> inactive.
223Obviously the path of a ticket can get more complicated than this, which
224is where transitions come into play.
225
226Transitions manage the flow of a ticket from status to status. This
227section of the configuration has keys, which are the current status,
228and values that define which other statuses the ticket can transition
229to. Here are the transitions we define for our order process.
230
231    Set( %Lifecycles, orders => {
232        # ...,
233        transitions => {
234            ''          => [qw(pending processing declined)],
235            pending     => [qw(processing declined deleted)],
236            processing  => [qw(pending declined delivery delivered deleted)],
237            delivery    => [qw(pending delivered returned deleted)],
238            delivered   => [qw(pending returned deleted)],
239            returned    => [qw(pending delivery deleted)],
240            deleted     => [qw(pending processing delivered delivery returned)],
241        },
242        # ...,
243    });
244
245If a ticket is in the delivered status, it doesn't make sense for it to
246transition to processing or declined since the customer already has the
247order. However, it can transition to returned since they could send it back.
248The configuration above defines this for RT.
249
250The C<''> entry defines the valid statuses when a ticket is created.
251
252Deleted is a special status in RT that allows you to remove a ticket from
253active use. You may need to do this if a ticket is created by mistake, or
254a duplicate is created. Once deleted, a ticket will never show up in search
255results. As you can see, the system will allow you to
256transition to deleted from any status.
257
258=head3 Rights and Access Control
259
260Your workflow may have several people working on tickets at different
261steps, and for some you may want to make sure only certain users
262can perform certain actions. For example, the company may have a rule
263that only the quality assurance team is allowed to approve (or decline)
264an order for delivery.
265
266You can apply labels to transitions and assign rights to them to allow
267you to apply this sort of access control. This is done with a rights
268entry:
269
270    Set( %Lifecycles, orders => {
271        # ...,
272        rights => {
273            '* -> declined' => 'DeclineOrder',
274            '* -> delivery' => 'ApproveOrder',
275        },
276        # ...,
277    });
278
279This configuration tells RT to require the right DeclineOrder for a
280transition from any status (C<*>) to C<declined>. The ApproveOrder
281right is similar, but for C<delivery>. These rights take the place of
282the standard ModifyTicket right, not in addition to it, so keep that
283in mind when creating  and assigning new rights.
284
285Once these rights are configured and loaded (by restarting the web
286server), they can be assigned in the web UI to groups, queues, and users.
287The rights show up on the rights pages in a Status tab alongside the
288standard RT rights tabs.
289
290=for html <img alt="Lifecycle group rights" src="../images/global-lifecycle-group-rights.png">
291
292=for :text [Lifecycle group rights F<docs/images/global-lifecycle-group-rights.png>]
293
294=for :man [Lifecycle group rights F<docs/images/global-lifecycle-group-rights.png>]
295
296After a status transition right is granted, users with the right will see
297the status in the drop-down, and possibly any related actions (see
298L</Actions>).
299
300=head3 Default Status
301
302There are interfaces to RT from which it isn't possible to define a status,
303like sending an email to create a ticket, but tickets
304require a status. To handle these cases, you can set
305default status values for RT to use when the user doesn't explicitly set
306a value.
307
308Looking at the defaults section in the standard RT configuration,
309you can see the events for which you can define a default status.
310For example, 'on_create' => 'new' automatically gives newly created tickets
311a C<new> status when the requestor doesn't supply a status. We can do the same
312for our process.
313
314    Set( %Lifecycles, orders => {
315        defaults => {
316            on_create => 'pending',
317        },
318        # ...,
319    });
320
321Only a small number of defaults are needed because in practice there are
322relatively few cases where a ticket will find itself without a status or
323in an ambiguous state.
324
325=head3 Actions
326
327To customize how transitions are presented in RT, lifecycles have an
328C<actions> section where you can customize how an action (e.g. changing
329status from new -> open) looks and functions. You can customize the action's
330label, which is how it appears to users, and the type of update, either comment
331or reply. As an example, in the default RT configuration the action
332"new -> open" has the default label "Open it" and an update value of C<Respond>.
333
334Using the lifecycles configuration, you can change the label to anything you
335like. You can set the update option to C<Comment> or C<Respond>, which tells RT
336to process the action as a comment (not sent to requestors) or a reply (sent
337to requestors).
338
339This part of the lifecycles configuration replaces the previous
340C<$ResolveDefaultUpdateType> configuration value. To mimic that option, set
341the update type to C<Comment> for all transitions to C<resolved>.
342
343Here is an example of a change we might make for our order process:
344
345    Set( %Lifecycles, orders => {
346        # ...,
347        actions => [
348            'pending -> processing' => {
349                label  => 'Open For Processing',
350                update => 'Comment',
351            },
352            'pending -> declined' => {
353                label  => 'Decline',
354                update => 'Respond',
355            },
356            # ...
357        ],
358        # ...
359    });
360
361Alternatively, supplying no update type results in a "quick"
362action that changes the status immediately without going through the
363ticket update page.  RT's default "Delete" action is a "quick" action,
364for example:
365
366    # from the RT "default" lifecycle
367    'new -> deleted'   => {
368        label  => 'Delete',
369    },
370
371If the transition has an associated right, it must be granted for a user to
372see the action. For example, if we give a group the DeclineOrder right as
373shown in the earlier example, members of that group will see a Decline option
374in their Actions menu if a ticket has a pending status. The
375L</"Full Configuration"> at the end shows other action entries that
376make the Decline option available in more cases.
377
378=for html <img alt="Action menu decline" src="../images/action-decline.png">
379
380=for :text [Action menu decline F<docs/images/action-decline.png>]
381
382=for :man [Action menu decline F<docs/images/action-decline.png>]
383
384=head3 Mapping Between Queues
385
386As we've demonstrated, each queue can have its own custom lifecycle, but
387in RT you sometimes want to move a ticket from one queue to another.
388A ticket will have a status in a given queue, but that status may not
389exist in another queue you want to move the ticket to, or it may exist
390but mean something different. To allow tickets to move between queues with
391different lifecycles, RT needs to know how to set the status appropriately.
392
393The lifecycle configuration has a C<__maps__> entry to allow you to
394specify the mappings you want between different queues. Sometimes statuses
395between queues don't or can't match perfectly, but if you need to move
396tickets between those queues, it's important that you provide a complete
397mapping, defining the most sensible mapping you can.
398
399If you don't provide a mapping, users will see an error when they try to
400move a ticket between queues with different lifecycles but no mapping.
401
402    Set( %Lifecycles,
403        orders => {
404            # ...
405        },
406        __maps__ => {
407            'default -> orders' => {
408                'new'  => 'pending',
409                'open' => 'processing',
410                # ...,
411            },
412            'orders -> default' => {
413                'pending'    => 'new',
414                'processing' => 'open',
415                # ...,
416            },
417            # ...,
418        },
419        # ...,
420    });
421
422In the example above, we first define mappings between the default queue and
423our new orders queue. The second block defines the reverse for tickets that
424might be moved from the orders queue to a queue that uses the default lifecycle.
425
426=head3 Full Configuration
427
428Here is the full configuration if you want to add it to your RT instance
429to experiment.
430
431    Set(%Lifecycles,
432
433        # 'orders' shows up as a lifecycle choice when you create a new
434        # queue or modify an existing one
435        orders => {
436            # All the appropriate order statuses
437            initial         => [ 'pending' ],
438            active          => [ 'processing', 'delivery' ],
439            inactive        => [ 'delivered', 'returned', 'declined', 'deleted' ],
440
441            # Default order statuses for certain actions
442            defaults => {
443                on_create => 'pending',
444            },
445
446            # Status change restrictions
447            transitions => {
448                ''          => [qw(pending processing declined)],
449                pending     => [qw(processing declined deleted)],
450                processing  => [qw(pending declined delivery delivered deleted)],
451                delivery    => [qw(pending delivered returned deleted)],
452                delivered   => [qw(pending returned deleted)],
453                returned    => [qw(pending delivery deleted)],
454                deleted     => [qw(pending processing delivered delivery returned)],
455            },
456
457            # Rights for different actions
458            rights => {
459
460                # These rights are in the default lifecycle
461                '* -> deleted'  => 'DeleteTicket',
462                '* -> *'        => 'ModifyTicket',
463
464                # Maybe we want to create rights to keep QA rigid
465                '* -> declined' => 'DeclineOrder',
466                '* -> delivery' => 'ApproveOrder',
467            },
468
469            # Actions for the web UI
470            actions => [
471                'pending -> processing' => {
472                    label  => 'Open For Processing',
473                    update => 'Comment',
474                },
475                'pending -> declined' => {
476                    label  => 'Decline',
477                    update => 'Respond',
478                },
479                'pending -> deleted' => {
480                    label  => 'Delete',
481                },
482                'processing -> declined' => {
483                    label  => 'Decline',
484                    update => 'Respond',
485                },
486                'processing -> delivery' => {
487                    label  => 'Out for delivery',
488                    update => 'Comment',
489                },
490                'delivery -> delivered' => {
491                    label  => 'Mark as delivered',
492                    update => 'Comment',
493                },
494                'delivery -> returned' => {
495                    label  => 'Returned to Manufacturer',
496                    update => 'Respond',
497                },
498                'delivered -> returned' => {
499                    label  => 'Returned to Manufacturer',
500                    update => 'Respond',
501                },
502                'returned -> delivery' => {
503                    label  => 'Re-deliver Order',
504                    update => 'Respond',
505                },
506                'deleted -> pending' => {
507                    label  => 'Undelete',
508                    update => 'Respond',
509                },
510            ],
511        },
512
513        # Status mapping different different lifecycles
514        __maps__ => {
515            'default -> orders' => {
516                'new'      => 'pending',
517                'open'     => 'processing',
518                'stalled'  => 'processing',
519                'resolved' => 'delivered',
520                'rejected' => 'declined',
521                'deleted'  => 'deleted',
522            },
523            'orders -> default' => {
524                'pending'    => 'new',
525                'processing' => 'open',
526                'delivered'  => 'resolved',
527                'delivery'   => 'open',
528                'returned'   => 'open', # closest matching we have in 'default'
529                'declined'   => 'rejected',
530                'deleted'    => 'deleted',
531            },
532        },
533    );
534
535Here is an example history of a ticket following this lifecycle:
536
537=for html <img alt="Lifecycle history" src="../images/order-history-example.png">
538
539=for :text [Lifecycle history F<docs/images/order-history-example.png>]
540
541=for :man [Lifecycle history F<docs/images/order-history-example.png>]
542