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