1 =head1 Ticket Lifecycles
3 By default, RT comes with ticket statuses that work for many types
4 of workflows: new, open, stalled, resolved, rejected, and deleted.
5 But there can be any number of workflows where these status values
6 don't completely fit. RT allows you to add new custom status values and
7 define their behavior with a feature called Lifecycles.
9 This guide demonstrates lifecycles using an order fulfillment
10 system as a real-world example. You can find full lifecycles
11 documentation in L<RT_Config/Lifecycles>.
13 As with all RT custom configuration, if you are customizing the RT
14 lifecycle, make your changes in your C<RT_SiteConfig.pm> file, not
15 directly in C<RT_Config.pm>. If you are adding a new lifecycle, you can
18 Set(%Lifecycles, my_new_lifecycle => { ... } );
20 The detailed configuration options are discussed below. Once you add it
21 and restart the server, the new lifecycle will be available on the
22 queue configuration page.
24 If you want to modify the default lifecycle, you can copy it from
25 C<RT_Config.pm>, paste it into C<RT_SiteConfig.pm> and make your
28 =head1 Order Processing Example
30 To show how you might use custom lifecycles, we're going to configure
31 an RT lifecycle to process orders of some sort. In our order example,
32 each ticket in the queue is considered a separate order and the orders
33 have the following statuses:
39 The order just came in untouched, pending purchase validation
43 The order is being looked at for transaction processing
47 The order is out for delivery
51 The order was successfully delivered to its destination
55 The order was delivered but subsequently refunded
59 There was an error in the process validation and the order was denied purchase
63 In this particular example, the only status an order can start with is
64 'pending.' When a process coordinator chooses to take this order, it
65 goes into processing. The order can then either be delivered or denied
66 processing. Once denied, the lifecycle for that order ends. If it is
67 delivered, the order can still be refunded.
69 The following sections walk through each part of the configuration.
70 You can find the full configuration at the end in case you want to
71 see the exact syntax or use it to experiment with.
73 =head2 Defining Status Values
75 Every queue has a lifecycle assigned to it. Without changing any
76 configuration, you are given two lifecycles to choose from: "default"
77 and "approvals." The approvals lifecycle is used by the internal
78 approvals queue, and should not be changed or used by other queues. Do
79 not modify the approvals lifecycle unless you fully understand how RT
82 =for html <img alt="Lifecycle choices" src="../images/lifecycle-choices.png">
84 =for :text [Lifecycle choices F<docs/images/lifecycle-choices.png>]
86 =for :man [Lifecycle choices F<docs/images/lifecycle-choices.png>]
88 In RT 4.0, the C<@ActiveStatus> and C<@InactiveStatus> configurations
89 which were previously available are gone. The logic defined by those
90 options is now a subset of RT's lifecycle features, as described here.
92 A ticket naturally has three states: initial (I<new>), active (I<open> and
93 I<stalled>), and inactive (I<resolved>, I<rejected>, and I<deleted>). These
94 default settings look like this in the C<RT_Config.pm> file:
98 active => [ 'open', 'stalled' ],
99 inactive => [ 'resolved', 'rejected', 'deleted' ],
101 The initial state is the default starting place for new tickets, although
102 you can create tickets with other statuses. Initial is generally used
103 to acknowledge that a request has been made, but not yet acted on. RT
104 sets the Started date on a ticket when it is moved out of the initial state.
106 Active tickets are currently being worked on, inactive tickets have reached
107 some final state. By default, inactive tickets don't show up in search
108 results. The AutoOpen action sets a ticket's status to the first active
109 status. You can find more details in L<RT_Config/"Lifecycle definitions">.
111 Now we want to set up some statuses appropriate for order fulfillment,
112 so we create a new top-level key called C<orders> and add our new status
115 Set( %Lifecycles, orders => {
116 initial => [ 'pending' ],
117 active => [ 'processing', 'delivery' ],
118 inactive => [ 'delivered', 'returned', 'declined', 'deleted' ],
122 We still use the initial, active and inactive categories, but we are
123 able to define status values that are appropriate for the workflow
124 we want to create. This should make the system more intuitive for users.
128 The typical lifecycle follows the path initial -> active -> inactive.
129 Obviously the path of a ticket can get more complicated than this, which
130 is where transitions come into play.
132 Transitions manage the flow of a ticket from status to status. This
133 section of the configuration has keys, which are the current status,
134 and values that define which other statuses the ticket can transition
135 to. Here are the transitions we define for our order process.
137 Set( %Lifecycles, orders => {
140 '' => [qw(pending processing declined)],
141 pending => [qw(processing declined deleted)],
142 processing => [qw(pending declined delivery delivered deleted)],
143 delivery => [qw(pending delivered returned deleted)],
144 delivered => [qw(pending returned deleted)],
145 returned => [qw(pending delivery deleted)],
146 deleted => [qw(pending processing delivered delivery returned)],
151 If a ticket is in the delivered status, it doesn't make sense for it to
152 transition to processing or declined since the customer already has the
153 order. However, it can transition to returned since they could send it back.
154 The configuration above defines this for RT.
156 The C<''> entry defines the valid statuses when a ticket is created.
158 Deleted is a special status in RT that allows you to remove a ticket from
159 active use. You may need to do this if a ticket is created by mistake, or
160 a duplicate is created. Once deleted, a ticket will never show up in search
161 results. As you can see, the system will allow you to
162 transition to deleted from any status.
164 =head2 Rights and Access Control
166 Your workflow may have several people working on tickets at different
167 steps, and for some you may want to make sure only certain users
168 can perform certain actions. For example, the company may have a rule
169 that only the quality assurance team is allowed to approve (or decline)
170 an order for delivery.
172 You can apply labels to transitions and assign rights to them to allow
173 you to apply this sort of access control. This is done with a rights
176 Set( %Lifecycles, orders => {
179 '* -> declined' => 'DeclineOrder',
180 '* -> delivery' => 'ApproveOrder',
185 This configuration tells RT to require the right DeclineOrder for a
186 transition from any status (C<*>) to C<declined>. The ApproveOrder
187 right is similar, but for C<delivery>. These rights take the place of
188 the standard ModifyTicket right, not in addition to it, so keep that
189 in mind when creating and assigning new rights.
191 Once these rights are configured and loaded (by restarting the web
192 server), they can be assigned in the web UI to groups, queues, and users.
193 The rights show up on the rights pages in a Status tab alongside the
194 standard RT rights tabs.
196 =for html <img alt="Lifecycle group rights" src="../images/global-lifecycle-group-rights.png">
198 =for :text [Lifecycle group rights F<docs/images/global-lifecycle-group-rights.png>]
200 =for :man [Lifecycle group rights F<docs/images/global-lifecycle-group-rights.png>]
202 After a status transition right is granted, users with the right will see
203 the status in the drop-down, and possibly any related actions (see
206 =head2 Default Status
208 There are interfaces to RT from which it isn't possible to define a status,
209 like sending an email to create a ticket, but tickets
210 require a status. To handle these cases, you can set
211 default status values for RT to use when the user doesn't explicitly set
214 Looking at the defaults section in the standard RT configuration,
215 you can see the events for which you can define a default status.
216 For example, 'on_create' => 'new' automatically gives newly created tickets
217 a C<new> status when the requestor doesn't supply a status. We can do the same
220 Set( %Lifecycles, orders => {
222 on_create => 'pending',
227 Only a small number of defaults are needed because in practice there are
228 relatively few cases where a ticket will find itself without a status or
229 in an ambiguous state.
233 To customize how transitions are presented in RT, lifecycles have an
234 C<actions> section where you can customize how an action (e.g. changing
235 status from new -> open) looks and functions. You can customize the action's
236 label, which is how it appears to users, and the type of update, either comment
237 or reply. As an example, in the default RT configuration the action
238 "new -> open" has the default label "Open it" and an update value of C<Respond>.
240 Using the lifecycles configuration, you can change the label to anything you
241 like. You can set the update option to C<Comment> or C<Respond>, which tells RT
242 to process the action as a comment (not sent to requestors) or a reply (sent
245 This part of the lifecycles configuration replaces the previous
246 C<$ResolveDefaultUpdateType> configuration value. To mimic that option, set
247 the update type to C<Comment> for all transitions to C<resolved>.
249 Here is an example of a change we might make for our order process:
251 Set( %Lifecycles, orders => {
254 'pending -> processing' => {
255 label => 'Open For Processing',
258 'pending -> declined' => {
267 Alternatively, supplying no update type results in a "quick"
268 action that changes the status immediately without going through the
269 ticket update page. RT's default "Delete" action is a "quick" action,
272 # from the RT "default" lifecycle
273 'new -> deleted' => {
277 If the transition has an associated right, it must be granted for a user to
278 see the action. For example, if we give a group the DeclineOrder right as
279 shown in the earlier example, members of that group will see a Decline option
280 in their Actions menu if a ticket has a pending status. The
281 L</"Full Configuration"> at the end shows other action entries that
282 make the Decline option available in more cases.
284 =for html <img alt="Action menu decline" src="../images/action-decline.png">
286 =for :text [Action menu decline F<docs/images/action-decline.png>]
288 =for :man [Action menu decline F<docs/images/action-decline.png>]
290 =head2 Mapping Between Queues
292 As we've demonstrated, each queue can have its own custom lifecycle, but
293 in RT you sometimes want to move a ticket from one queue to another.
294 A ticket will have a status in a given queue, but that status may not
295 exist in another queue you want to move the ticket to, or it may exist
296 but mean something different. To allow tickets to move between queues with
297 different lifecycles, RT needs to know how to set the status appropriately.
299 The lifecycle configuration has a C<__maps__> entry to allow you to
300 specify the mappings you want between different queues. Sometimes statuses
301 between queues don't or can't match perfectly, but if you need to move
302 tickets between those queues, it's important that you provide a complete
303 mapping, defining the most sensible mapping you can.
305 If you don't provide a mapping, users will see an error when they try to
306 move a ticket between queues with different lifecycles but no mapping.
308 Set( %Lifecycles, orders => {
311 'default -> orders' => {
313 'open' => 'processing',
316 'orders -> default' => {
318 'processing' => 'open',
326 In the example above, we first define mappings between the default queue and
327 our new orders queue. The second block defines the reverse for tickets that
328 might be moved from the orders queue to a queue that uses the default lifecycle.
330 =head2 Full Configuration
332 Here is the full configuration if you want to add it to your RT instance
337 # 'orders' shows up as a lifecycle choice when you create a new
338 # queue or modify an existing one
340 # All the appropriate order statuses
341 initial => [ 'pending' ],
342 active => [ 'processing', 'delivery' ],
343 inactive => [ 'delivered', 'returned', 'declined' ],
345 # Default order statuses for certain actions
347 on_create => 'pending',
350 # Status change restrictions
352 '' => [qw(pending processing declined)],
353 pending => [qw(processing declined deleted)],
354 processing => [qw(pending declined delivery delivered deleted)],
355 delivery => [qw(pending delivered returned deleted)],
356 delivered => [qw(pending returned deleted)],
357 returned => [qw(pending delivery deleted)],
358 deleted => [qw(pending processing delivered delivery returned)],
361 # Rights for different actions
364 # These rights are in the default lifecycle
365 '* -> deleted' => 'DeleteTicket',
366 '* -> *' => 'ModifyTicket',
368 # Maybe we want to create rights to keep QA rigid
369 '* -> declined' => 'DeclineOrder',
370 '* -> delivery' => 'ApproveOrder',
373 # Actions for the web UI
375 'pending -> processing' => {
376 label => 'Open For Processing',
379 'pending -> delivered' => {
380 label => 'Mark as being delivered',
383 'pending -> declined' => {
387 'pending -> deleted' => {
390 'processing -> declined' => {
394 'processing -> delivery' => {
395 label => 'Out for delivery',
398 'delivery -> delivered' => {
399 label => 'Mark as delivered',
402 'delivery -> returned' => {
403 label => 'Returned to Manufacturer',
406 'delivered -> returned' => {
407 label => 'Returned to Manufacturer',
410 'returned -> delivery' => {
411 label => 'Re-deliver Order',
414 'deleted -> pending' => {
421 # Status mapping different different lifecycles
423 'default -> orders' => {
425 'open' => 'processing',
426 'stalled' => 'processing',
427 'resolved' => 'delivered',
428 'rejected' => 'declined',
429 'deleted' => 'deleted',
431 'orders -> default' => {
433 'processing' => 'open',
434 'delivered' => 'resolved',
435 'returned' => 'open', # closest matching we have in 'default'
436 'declined' => 'rejected',
437 'deleted' => 'deleted',
442 Here is an example history of a ticket following this lifecycle:
444 =for html <img alt="Lifecycle history" src="../images/order-history-example.png">
446 =for :text [Lifecycle history F<docs/images/order-history-example.png>]
448 =for :man [Lifecycle history F<docs/images/order-history-example.png>]