NAME
    RT-Extension-MandatoryOnTransition - Require core fields and ticket
    custom fields on status transitions

RT VERSION
    Works with RT 6.

    For RT 5, use the latest 0.X version.

DESCRIPTION
    This RT extension enforces that certain fields have values before
    tickets are explicitly moved to or from specified statuses. If you list
    custom fields which must have a value before a ticket is resolved, those
    custom fields will automatically show up on the "Resolve" page. The
    reply/comment won't be allowed until a value is provided.

    See the configuration example under "INSTALLATION".

  Supported fields
    This extension only enforces mandatory-ness on defined status
    transitions. It also supports defining mandatory fields when
    transitioning a ticket from one queue to another.

   Basics
    Currently the following are supported:

    Content
        Requires an update message (reply/comment text) before the
        transition.

    TimeWorked
        Requires the ticket has a non-zero amount of Time Worked recorded
        already or that time worked will be recorded with the current
        reply/comment in the Worked field on the update page.

    TimeTaken
        Requires that the Worked field on the update page is non-zero.

    A larger set of basic fields may be supported in future releases. If
    you'd like to see additional fields added, please email your request to
    the bug address at the bottom of this documentation.

   Custom fields
    Ticket custom fields of all types are supported.

CAVEATS
  Custom field validation (*Input must match [Mandatory]*)
    The custom fields enforced by this extension are validated by the
    standard RT rules. If you've set Validation patterns for your custom
    fields, those will be checked before mandatory-ness is checked. Setting
    a CFs Validation to (?#Mandatory). will not magically make it enforced
    by this extension.

  Not all pages where you can change status are supported
    SelfService, for example. See "TODO" for others.

  Multiple-entry CFs do not play well with must_be and must_not_be
    The must_be and must_not_be configurations are currently not
    well-defined for multiply-valued CFs. At current, only their first value
    is validated against the configured whitelist or blacklist.

INSTALLATION
    perl Makefile.PL
    make
    make install
        May need root permissions

    Edit your /opt/rt6/etc/RT_SiteConfig.pm
        Add this line to your RT_SiteConfig.pm:

            Plugin('RT::Extension::MandatoryOnTransition');

    Clear your mason cache
            rm -rf /opt/rt6/var/mason_data/obj

    Restart your webserver

    If running with RTIR, the Plugin line for RTIR must come first in your
    configuration as shown below.

        Plugin('RT::IR');
        Plugin('RT::Extension::MandatoryOnTransition');

CONFIGURATION
    To define which fields should be mandatory on certain status changes
    (either globally or in a specific queue) use the %MandatoryOnTransition
    config option. This option takes the generic form of:

        Set( %MandatoryOnTransition,
            'QueueName' => {
                'from -> to' => [ 'BasicField', 'CF.MyField', 'CustomRole.MyRole' ],
            },
        );

    from and to are expected to be valid status names. from may also be *
    which will apply to any status and also tickets about to be created with
    status to. If you need to express rules that apply only on ticket
    creation and not on updates, you can use the special from value of
    '__CREATE__'. For example,

        Set( %MandatoryOnTransition,
            'MyQueue' => {
                '__CREATE__ -> new' => [ 'CF.MyField1' ],
                '* -> open' => [ 'CF.MyField2', 'CF.MyField3' ],
            },
        );

    would require CF.MyField1 on ticket creation and CF.MyField2 and
    CF.MyField3 on any other transition to open. The to status for CREATE
    rules must be a valid create status in the lifecycle.

    The fallback for queues without specific rules is specified with '*'
    where the queue name would normally be.

  Requiring Any Value
    Below is an example which requires 1) time worked and filling in a
    custom field named Resolution before resolving tickets in the Helpdesk
    queue and 2) a Category selection before resolving tickets in every
    other queue.

        Set( %MandatoryOnTransition,
            Helpdesk => {
                '* -> resolved'      => ['TimeWorked', 'CF.Resolution', 'CustomRole.Analyst'],
            },
            '*' => {
                '* -> resolved' => ['CF.Category'],
            },
        );

    The transition syntax is similar to that found in RT's Lifecycles. See
    perldoc /opt/rt6/etc/RT_Config.pm.

  Requiring role values
    You can require any core or custom role on a RT::Ticket object, below is
    an example of requiring a custom role "customer" be set on transition
    from open and the owner also be set for the ticket on transition from a
    status of open.

        Set( %MandatoryOnTransition,
            'General' => {
                '* -> resolved' => ['CustomRole.customer', 'Owner'],
            },
        );

  Role Membership in a Group
    Roles can require the members of the role to also be a member of a group
    before satisfying to mandatory condition. Below we require that the
    Owner role be set and that the member it is set to is a member of the
    group 'SupportReps' or 'Admins'.

        Set( %MandatoryOnTransition,
            'General' => {
                'open -> *' => ['Owner'],
                'Owner' => { transition => 'open -> *', group => ['SupportReps', 'Admins'] },
            }
        );

  Restrictions on Queue Transitions
    The default behavior for MandatoryOnTransition operates on status
    transitions, so a change from new to open or from open to resolved. It
    also supports making fields mandatory when transitioning from one queue
    to another. To define fields that are required when changing the queue
    for a ticket, add an entry to the configuration like this:

        Set( %MandatoryOnTransition,
            Helpdesk => {
                'Engineering' => ['CF.Category'],
            },
        );

    The key is the destination queue and the values are the mandatory
    fields. In this case, before a user can move a ticket from the Helpdesk
    queue to the Engineering queue, they must provide a value for Category,
    possibly something like "bug" or "feature request".

    Note that this configuration makes the most sense if the custom fields
    are applied to both queues. Otherwise the users on the destination queue
    won't be able to see the required values.

  Requiring or Restricting Specific Values
    Sometimes you want to restrict a transition if a field has a specific
    value (maybe a ticket can't be resolved if System Status = down) or
    require a specific value to to allow a transition (ticket can't be
    resolved unless a problem was fixed). To enforce specific values, you
    can add the following:

        Set( %MandatoryOnTransition,
            Helpdesk => {
                '* -> resolved' => ['TimeWorked', 'CF.Resolution', 'CF.System Status'],
                'CF.Resolution' => { transition => '* -> resolved', must_be => ['fixed', 'not an issue'] },
                'CF.System Status' => { transition => '* -> resolved', must_not_be => ['reduced', 'down']}
            },
        );

    This will then enforce both that the value is set and that it either has
    one of the required values on the configured transition or does not have
    one of the restricted values.

    Note that you need to specify the transition the rule applies to since a
    given queue configuration could have multiple transition rules.

  $ShowAllCustomFieldsOnMandatoryUpdate
    By default, this extension shows only the mandatory fields on the update
    page to make it easy for users to fill them out when completing an
    action. If you would like to show all custom fields rather than just the
    mandatory ones, use this configuration option. You can set it like this:

        Set($ShowAllCustomFieldsOnMandatoryUpdate, 1);

IMPLEMENTATION DETAILS
    If you're just using this module on your own RT instance, you should
    stop reading now. You don't need to know about the implementation
    details unless you're writing a patch against this extension.

  Package variables
    @CORE_SUPPORTED
        The core (basic) fields supported by the extension. Anything else
        configured but not in this list is stripped.

    @CORE_TICKET
        The core (basic) fields which should be called as methods on ticket
        objects to check for current values.

    %CORE_FOR_UPDATE
        A mapping which translates core fields into their form input names.
        For example, Content is submitted as UpdateContent. All fields must
        be mapped, even if they are named exactly as listed in
        @CORE_SUPPORTED. A supported field which doesn't appear in the
        mapping is skipped, the implication being that it isn't available
        during update.

        If your core field is called different things on Update.html and
        Modify.html you will need to modify the Modify.html/Default callback
        so the the extension knows what field to use. Look at TimeWorked vs
        UpdateTimeWorked for an example.

    %CORE_FOR_CREATE
        A mapping similar to %CORE_FOR_UPDATE but consulted during ticket
        creation. The same rules and restrictions apply.

    If you're looking to add support for other core fields, you'll need to
    push into @CORE_SUPPORTED and possibly @CORE_TICKET. You'll also need to
    add a pair to %CORE_FOR_UPDATE and/or %CORE_FOR_CREATE.

  Methods
   RequiredFields
    Returns three array refs of required fields for the described status
    transition. The first is core fields, the second is CF names, the third
    is roles. Returns empty array refs on error or if nothing is required.

    A fourth returned parameter is a hashref of must-have values for custom
    fields.

    The fifth parameter is a hashref of groups a role member must be in.

    Takes a paramhash with the keys Ticket, Queue, From, and To. Ticket
    should be an object. Queue should be a name. From and To should be
    statuses. If you specify Ticket, only To is otherwise necessary. If you
    omit Ticket, From, To, and Queue are all necessary.

    The first transition found in the order below is used:

        from -> to
        *    -> to
        from -> *

   CheckMandatoryFields
    Pulls core and custom mandatory fields from the configuration and checks
    that they have a value set before transitioning to the requested status.

    Accepts a paramhash of values: ARGSRef => Reference to Mason ARGS Ticket
    => ticket object being updated Queue => Queue object for the queue in
    which a new ticket is being created From => Ticket status transitioning
    from To => Ticket status transitioning to

    Works for both create, where no ticket exists yet, and update on an
    existing ticket. ARGSRef is required for both.

    For create, you must also pass Queue, From, and To. In this case, the
    From status should be the special flag value of '__CREATE__'.

    Update requires only Ticket and To since From can be fetched from the
    ticket object.

   Config
    Takes a queue name. Returns a hashref for the given queue (possibly
    using the fallback rules) which contains keys of transitions and values
    of arrayrefs of fields.

    You shouldn't need to use this directly.

TODO
    Enforcement on Create
            index.html / QuickCreate    - Not yet implemented.
            SelfService                 - Not yet implemented.

    Enforcement on other update pages
            SelfService - can't do it without patches to <form> POST + additional callbacks

    Full Validation of Configuration
            Check that all CFs are actually CFs applied to the indicated queues (or global). Also
            validate additional CF's with "must" configuration are defined in a transition.

    Allow empty values in "must" configuration
            When defining a list of "must be" or "must not be" values, there may be use cases where
            an empty value could be valid. Provide a way to specify and allow this.

AUTHOR
    Best Practical Solutions, LLC <modules@bestpractical.com>

BUGS
    All bugs should be reported via email to

        L<bug-RT-Extension-MandatoryOnTransition@rt.cpan.org|mailto:bug-RT-Extension-MandatoryOnTransition@rt.cpan.org>

    or via the web at

        L<rt.cpan.org|http://rt.cpan.org/Public/Dist/Display.html?Name=RT-Extension-MandatoryOnTransition>.

LICENSE AND COPYRIGHT
    This software is Copyright (c) 2012-2025 by Best Pracical Solutions,
    LLC.

    This is free software, licensed under:

      The GNU General Public License, Version 2, June 1991

