Jira Script: move issue to another project
Asked by Fabian:

Hello dear community,  
I have a problem and can't get any further. Maybe you can find the error or know the problem.  
I have a script that I want to use to move a JSM issue from one project to another Service Management project.

Use case 1) If I only change the request type, everything works and all adjustments are carried out by the script as desired.  
2) However, if the issue is to be moved to another project, it does not always work. Here I emphasise: "Not always".  
Because sometimes it does work.  
Problem: Unfortunately, I don't know why it sometimes works and then not.  
Maybe you can help me.

*** ** * ** ***

```
package MoveClass

import com.atlassian.crowd.embedded.api.Group
import com.atlassian.jira.util.ErrorCollection
import com.atlassian.jira.issue.IssueVerifier
import com.atlassian.jira.issue.history.ChangeLogUtils
import com.atlassian.jira.event.issue.IssueEventBundle
import org.ofbiz.core.entity.GenericValue
import com.atlassian.jira.issue.fields.layout.field.FieldLayoutItem
import com.atlassian.jira.issue.fields.OrderableField
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.history.ChangeItemBean
import com.atlassian.jira.config.ConstantsManager
import com.atlassian.jira.event.issue.IssueEventManager
import com.atlassian.jira.project.Project
import com.atlassian.jira.config.IssueTypeManager
import com.atlassian.jira.transaction.Transaction
import com.atlassian.jira.transaction.Txn
import com.atlassian.jira.event.type.EventType
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.AttachmentManager
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.fields.FieldManager
import com.atlassian.jira.issue.fields.layout.field.FieldLayoutManager
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.issuetype.IssueType
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.util.IssueUpdateBean
import com.atlassian.jira.issue.status.Status
import com.atlassian.jira.workflow.WorkflowManager
import com.atlassian.jira.workflow.JiraWorkflow
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.web.bean.MoveIssueBean
import com.atlassian.servicedesk.api.request.type.CustomerRequestType
import com.atlassian.servicedesk.api.request.type.CustomerRequestTypeQuery
import com.atlassian.servicedesk.api.request.type.CustomerRequestTypeService
import com.atlassian.servicedesk.api.requesttype.RequestType
import com.atlassian.servicedesk.api.requesttype.RequestTypeQuery
import com.atlassian.servicedesk.api.requesttype.RequestTypeService
import com.atlassian.servicedesk.api.user.UncheckedUser
import com.atlassian.servicedesk.api.user.UserFactory
import com.atlassian.servicedesk.api.util.paging.PagedRequest
import com.atlassian.servicedesk.api.util.paging.PagedResponse
import com.atlassian.servicedesk.api.util.paging.SimplePagedRequest
import com.atlassian.servicedesk.api.user.UserFactory
import com.atlassian.jira.event.issue.IssueEventBundleFactory
import groovy.util.logging.Log4j
import java.util.regex.Matcher
import java.util.regex.Pattern

@Log4j

public class Move {
    // String requestTypeName - Name of the desired request Type
    // boolean sameStatusIfNoMapping
    // Issue issue
    // String targetProjectKey
    // String targetIssueType
    public static String move(String requestTypeName, boolean sameStatusIfNoMapping, Issue issue, String targetProjectKey, String targetIssueType) throws Exception {

        boolean debug = true

        if(debug) {
            log.warn(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Start move issue")
            log.warn("sameStatusIfNoMapping:" + sameStatusIfNoMapping)
            log.warn("Issue: " + issue)
            log.warn("targetProjectKey: " + targetProjectKey)
            log.warn("targetIssueType: " + targetIssueType)
        }

        IssueManager issueManager          = ComponentAccessor.getIssueManager()
        IssueTypeManager issueTypeManager  = ComponentAccessor.getComponent(com.atlassian.jira.config.IssueTypeManager.class)
        WorkflowManager workflowManager    = ComponentAccessor.getWorkflowManager()
        JiraWorkflow workflowOriginal      = workflowManager.getWorkflow(issue)
        MutableIssue mIssueOriginal = issueManager.getIssueObject(issue.getId() as Long)

        Project projectTarget = null

        try{
            projectTarget = Projects.getByKey(targetProjectKey)
        }catch(Exception e) {
            throw new Exception (e.getMessage())
        }

        IssueType issueTypeTarget = issueTypeManager.getIssueTypes().find{ it.name == targetIssueType }

        if (issueTypeTarget == null) {
            throw new Exception ("No issue type with the name $targetIssueType where found.")
        }

        
        JiraWorkflow workflowTarget = workflowManager.getWorkflow(projectTarget.getId(), issueTypeTarget.getId())

        if(debug) log.warn("workflowTarget (95): " + workflowTarget + " || projectTarget: " + projectTarget.getId() + " issueTypeTarget id: " + issueTypeTarget.getId())

        // try catch new Exception throw
        // if (sameStatusIfNotInMapping && _targetWf.getLinkedStatusObjects().any { it.name.equalsIgnoreCase(originalIssueObject.status.name) }) {
        //        return originalIssueObject.status.name
        //    }

        ApplicationUser currentLoggedInUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
        AttachmentManager attachmentManager = ComponentAccessor.getAttachmentManager()

        IssueEventManager issueEventManager = ComponentAccessor.getIssueEventManager()
        IssueEventBundleFactory issueEventBundleFactory = ComponentAccessor.getComponent(com.atlassian.jira.event.issue.IssueEventBundleFactory.class)
        
        FieldManager fieldManager               = ComponentAccessor.getFieldManager()
        FieldLayoutManager fieldLayoutManager   = ComponentAccessor.getFieldLayoutManager()

        ConstantsManager constantManager        = ComponentAccessor.getConstantsManager()
        
        MutableIssue mIssueTarget   = issueManager.getIssueObject(mIssueOriginal.getId())
        Project projectOriginal     = issue.getProjectObject()

        IssueType issueTypeOriginal = issue.getIssueType()
        Status statusOriginal       = issue.getStatus()
        Status statusTarget = workflowTarget.getLinkedStatusObjects().find { it.name.equalsIgnoreCase(status_to) }

        if (statusTarget == null) {
            statusTarget = workflowTarget.getLinkedStatusObjects().first()
        }

        def getWorkflowForType = {Long pid, String itId ->
            workflowManager.getWorkflow(pid, itId)
        }

        def isWorkflowMatch = {String currentIssueTypeId, String issueTypeIdTarget ->
            return getWorkflowForType(projectOriginal.getId(), issueTypeOriginal.getId()).equals(getWorkflowForType(projectTarget.getId(), issueTypeIdTarget))
        }

        def migrateIssueToWorkflow = {MutableIssue issueToMigrate, JiraWorkflow targetWorkflow, Status targetStatus ->
            workflowManager.migrateIssueToWorkflow(issueToMigrate, targetWorkflow, targetStatus)
        }

        MoveIssueBean  moveIssueBean = new MoveIssueBean(constantManager, ComponentAccessor.getProjectManager())
        moveIssueBean.getFieldValuesHolder().put("project", projectTarget.getId())
        moveIssueBean.getFieldValuesHolder().put("issuetype", issueTypeTarget.getId())
        moveIssueBean.setCurrentStep(workflowOriginal.getLinkedStep(issue.getStatus()).getId())
        moveIssueBean.setSourceIssueKey(issue.getKey())
        moveIssueBean.setTargetStatusId(statusTarget.getId())
        moveIssueBean.setUpdatedIssue(mIssueTarget)

        if(debug) log.warn("moveIssueBean (141):" + moveIssueBean)

        def moveIssueDetails = { MutableIssue issueNew, String oldKey, String wfOriginalId ->

            if(debug) log.warn("moveIssuesDetails begin 143:")

            DefaultIssueChangeHolder changeHolder = new DefaultIssueChangeHolder()
            //IssueType issueTypeCurrent = constantManager.getIssueType(issueTypeOriginal.getId())
            //IssueType issueTypeTargetForMoveIssueDetail = constantManager.getIssueType(issueTypeTarget.getId())
            String newKey = issueNew.getKey()

            if(debug) log.warn("issueNew key (150): " + newKey)

            if(!projectOriginal.equals(projectTarget)) {
                if(debug) log.warn("!projectOriginal.equals(projectTarget) (152)")
                changeHolder.addChangeItem(new ChangeItemBean("jira", "project", projectOriginal.getId().toString(), projectOriginal.getName(), projectTarget.getId().toString(), projectTarget.getName()))
                changeHolder.addChangeItem(new ChangeItemBean("jira", "Key", (String)null, oldKey, (String)null, newKey));
            }   

            if(!issueTypeOriginal.equals(issueTypeTarget)) {    // isSubTask() fehlt - Zeile 314

                if(debug) log.warn("!issueTypeOrigininal.equals(issueTypeTarget) (158): ${issueTypeOriginal} equals ${issueTypeTarget} ?")
                changeHolder.addChangeItem(new ChangeItemBean("jira", "issuetype", issueTypeOriginal.getId(), issueTypeOriginal.getName(), issueTypeTarget.getId(), issueTypeTarget.getName()))
                issueNew.setIssueTypeObject(issueTypeTarget)
                if(debug) {
                    log.warn("issueTypeTarget: " + issueTypeTarget)
                    log.warn("issueNew.setIssueTypeObject: " + issueNew)
                }

            }

            if(!isWorkflowMatch(issueTypeOriginal.getId(), issueTypeTarget.getId())) {

                if(debug) log.warn("!isWorkflowMatch(issueTypeOriginal.getId() (168) --> ${issueTypeOriginal.getId()}, issueTypeTarget.getId()) --> ${issueTypeTarget.getId()} (166)")
                changeHolder.addChangeItem(new ChangeItemBean("jira", "Workflow", wfOriginalId, workflowManager.getWorkflow(issue).getName(), issueNew.getWorkflowId().toString(), getWorkflowForType(projectTarget.getId(), issueTypeTarget.getName()).getName()))

                if(!statusOriginal.equals(statusTarget)) {

                    if(debug) log.warn("!statusOriginal.equals(statusTarget) (171) ${statusOriginal} == ${statusTarget}")
                    changeHolder.addChangeItem(new ChangeItemBean("jira", "status", statusOriginal.getId(), statusOriginal.getName(), statusTarget.getId(), statusTarget.getName()))

                }

            }

            // Set request type on issue

            RequestType requestType = findRequestType(requestTypeName)

            CustomerRequestTypeService customerRequestTypeService = ComponentAccessor.getOSGiComponentInstanceOfType(CustomerRequestTypeService)
            CustomerRequestTypeQuery customerRequestTypeQuery     = customerRequestTypeService.newQueryBuilder().requestType(requestType).build()

            UserFactory userFactory     = ComponentAccessor.getOSGiComponentInstanceOfType(UserFactory)
            UncheckedUser uncheckedUser = userFactory.getUncheckedUser()

            log.warn("uncheckedUser: (184) ${uncheckedUser}")

            CustomerRequestType customerRequestType = customerRequestTypeService.getCustomerRequestType(uncheckedUser, customerRequestTypeQuery) as CustomerRequestType
            CustomFieldManager cfm  = ComponentAccessor.getCustomFieldManager()
            
            // Customfields that are to be filled or changed: 
            // cfCRT: Customer Request Type
            // cfNewFLag: New Information (Option field)
            // cfAssignedGroup: Group Picker field.
            CustomField cfCRT           = cfm.getCustomFieldObject(15403)
            CustomField cfNewFlag       = cfm.getCustomFieldObject(15513)
            CustomField cfAssignedGroup = cfm.getCustomFieldObject(10238)

            def collCurrentIssue = (Collection) issue.getCustomFieldValue(cfNewFlag)

            if (collCurrentIssue == null) {

                def cfConfigCurrentIssue = cfNewFlag.getRelevantConfig(issue)
                def optionsCurrentIssue  = ComponentAccessor.getOptionsManager().getOptions(cfConfigCurrentIssue)
                def optionsToSet         = optionsCurrentIssue.findAll { it.value == "Yes" }

                issueNew.setCustomFieldValue(cfNewFlag, optionsToSet)

                log.warn("setCustomFieldValue (197) ${cfNewFlag}")
            }

            // Set assigned group on issue

            Group group = identifyAssignedGroup(issueNew, requestType.getId())

            log.warn("identifyAssignedGroup (202): Group: ${group?.getName()}")

            issueNew.setAssignee(null)
            issueNew.setCustomFieldValue(cfCRT, customerRequestType)
            issueNew.setCustomFieldValue(cfAssignedGroup, [group])

            log.warn("setCustomFieldValue (206) ${cfAssignedGroup}")

            issueManager.updateIssue(currentLoggedInUser, issueNew, EventDispatchOption.DO_NOT_DISPATCH, false) // statt issueNew.store()

            if(debug) {
                log.warn("issueManage.updateIssue (210): currentLoggedInUser: " + currentLoggedInUser.getDisplayName())
                log.warn("IssueNew key (211): " + issueNew.getKey())
            }

            Map<String, ModifiedValue> modifiedFields = issueNew.getModifiedFields()

            Iterator iterator                         = modifiedFields.keySet().iterator()
            if(debug) log.warn("modifiedFields (216): ${modifiedFields} --> while iterator.hasNext():")

            while(iterator.hasNext()) {
                String fieldId = (String) iterator.next()
                if(debug) log.warn("fieldId (220):" + fieldId)
                if(fieldManager.isOrderableField(fieldId)) {
                    if(debug) log.warn("isOrderableField (222):" + fieldManager.isOrderableField(fieldId))

                    OrderableField field            = fieldManager.getOrderableField(fieldId)
                    if(debug) log.warn("OrderableField (224):" + field)
                    FieldLayoutItem fieldLayoutItem = fieldLayoutManager.getFieldLayout(issueNew).getFieldLayoutItem(field)
                    field.updateValue(fieldLayoutItem, issueNew, (ModifiedValue) modifiedFields.get(fieldId), changeHolder)
                    if(debug) log.warn("field.updateValue (227)")
                }
            }

            issueNew.resetModifiedFields()

            if(debug) log.warn("issueNew.resetModifiedFields (232):" + issueNew)
            return changeHolder

        } // End moveIssueDetails

        def dispatchIssueUpdateEvents = { Issue issueNew, GenericValue updateLog, DefaultIssueChangeHolder issueChangeHolder ->

            if(debug) log.warn("dispatchIssueUpdateEvents begin: 237")
            if(updateLog != null && !issueChangeHolder.getChangeItems().isEmpty()) {

                Long eventTypeId;
                if(debug) log.warn("!projectOriginal.equals(projectTarget) (241): ${projectOriginal} equals ${projectTarget}?")
                if(!projectOriginal.equals(projectTarget)) {

                    eventTypeId = EventType.ISSUE_MOVED_ID
                    if(debug) log.warn("EventType:ISSUE_MOVED_ID")
                } else {
                    eventTypeId = EventType.ISSUE_UPDATED_ID
                    if(debug) log.warn("EventType.ISSUE_UPDATED_ID")

                }

                if(debug) log.warn("eventTypeid: " + eventTypeId)

                issueEventManager.dispatchRedundantEvent(eventTypeId, issueNew, currentLoggedInUser, updateLog, true, issueChangeHolder.isSubtasksUpdated())
                if(debug) log.warn("issueEventManage.dispatchRedundantEvent (252)")

                //def _issueChangeHolderTransformerClazz = ComponentAccessor.getClassLoader().loadClass("com.atlassian.jira.issue.util.transformers.IssueChangeHolderTransformer")

                IssueUpdateBean issueUpdateBean = new IssueUpdateBean(issueNew, issue, eventTypeId, currentLoggedInUser)
                if(debug) log.warn("issueUpdateBean (256)")

                IssueEventBundle issueEventBundle = issueEventBundleFactory.createIssueUpdateEventBundle(issueNew, updateLog, issueUpdateBean, currentLoggedInUser)
                issueEventManager.dispatchEvent(issueEventBundle)

                if(debug) log.warn("issueEventManager.dispatchEvent (259)")

            }

        } // End dispatchIssueUpdateEvents

        def moveIssueInTxn = {Transaction txn, MutableIssue originalIssueObject1, MutableIssue updatedIssueObject, String originalWfId ->

            if(debug) log.warn("moveIssueInTxn begin 264:")
            if(!isWorkflowMatch(issueTypeOriginal.getId(), issueTypeTarget.getId())) {

                if(debug) log.warn("!isWorkflowMatch (266): issueTypeOriginal.getId(): ${issueTypeOriginal.getId()} issueTypeTarget ID: ${issueTypeTarget.getId()}")
                migrateIssueToWorkflow(updatedIssueObject, workflowManager.getWorkflow(projectTarget.getId(), issueTypeTarget.getId()), statusTarget)
                if(debug) log.warn("migrateIssueToWorkflow...")

            }

            updatedIssueObject.setUpdated(new java.sql.Timestamp(System.currentTimeMillis()))
            if(debug) log.warn("updateIssueObject.setUpdated (272)" + updatedIssueObject)

            def issueChangeHolder = moveIssueDetails(updatedIssueObject, originalIssueObject1.getKey(), originalWfId);
            if(debug) log.warn("issueChangeHolder (274) = moveIssueDetails: ${updatedIssueObject}, originalIssueObject1.getKey(): ${originalIssueObject1.getKey()}, ${originalWfId}")

            GenericValue updateLog = ChangeLogUtils.createChangeGroup(currentLoggedInUser, originalIssueObject1.getGenericValue(), updatedIssueObject.getGenericValue(), issueChangeHolder.getChangeItems(), false);
            if(debug) log.warn("updatelog (276):" + updateLog)

            // Set info that issue were moved.
            if(projectOriginal.equals(projectTarget)) {

                if(debug) log.warn("projectOriginal.equals(projectTarget) (280): ${projectOriginal} equals ${projectTarget} ")
                issueManager.recordMovedIssueKey(originalIssueObject1)
                if(debug) log.warn("issueManager.recordMovedIssueKey(originalIssueObject1) (282): ${originalIssueObject1}")

            }

            txn.commit()
            dispatchIssueUpdateEvents(updatedIssueObject, updateLog, issueChangeHolder)
            if(debug) log.warn("dispatchIssueUpdateEvents: (286): ${updatedIssueObject}, UpdateLog: ${updateLog}, ${issueChangeHolder}")

        } // End moveIssueInTxn

        String sourceIssueKey = moveIssueBean.getSourceIssueKey()

        if(debug) log.warn("sourceIssueKey (290): ${sourceIssueKey}")
        if(!issue.getKey().equals(sourceIssueKey)) {
            throw new Exception("An error occured in line 292. issue key (${issue.getKey()}) is equal source key (${sourceIssueKey})")
        } else {
            String originalWfId             = null
            MutableIssue updatedIssueObject = moveIssueBean.getUpdatedIssue()
            if(debug) log.warn("originalWfId (296): " + originalWfId + " updatedIssueObject: " + updatedIssueObject)
            if (updatedIssueObject.getWorkflowId() != null) {
                originalWfId = updatedIssueObject.getWorkflowId().toString()
                if(debug) log.warn("updatedIssueObject.getWorkflowId() != null (299) - originalWfId: " + originalWfId)
            }

            IssueVerifier issueVerifier                  = new IssueVerifier()
            Map<String, String> workflowMigrationMapping = new HashMap()
            workflowMigrationMapping.put(updatedIssueObject.getStatus().getId(), statusTarget.getId())

            if(debug) log.warn("workflowMigrationMapping.put( (305) ${updatedIssueObject.getStatus().getId()}, ${statusTarget.getId()})")
            ErrorCollection errorCollection = issueVerifier.verifyIssue(issue, workflowMigrationMapping, true)
            if (errorCollection != null && errorCollection.hasAnyErrors()) {

                //addErrorCollection(errorCollection)
                if(debug) log.warn("errorCollection: " + errorCollection)
                throw new Exception("An workflow migration error occured.")
            } else {
                def txnClass = ComponentAccessor.getClassLoader().loadClass("com.atlassian.jira.transaction.Txn")
                def txn      = Txn.begin()
                if(!projectOriginal.equals(projectTarget)) {

                    if(debug) log.warn("!projectOriginal.equals(projectTarget) ${projectOriginal}, equals ${projectTarget}")
                    Project pTarget = projectTarget
                    updatedIssueObject.setProjectObject(pTarget)
                    if(debug) log.warn("updatedIssueObject.setProjectObject ${pTarget}")
                    long incCount = ComponentAccessor.getProjectManager().getNextId(pTarget)
                    if(debug) log.warn("incCount (322): ${incCount}")

                    updatedIssueObject.setNumber(Long.valueOf(incCount))
                    attachmentManager.moveAttachments(issue, updatedIssueObject.getKey())

                    if(debug) log.warn("attachmentManager.moveAttachments ()...")
                }

                try{

                    moveIssueInTxn(txn, mIssueOriginal, updatedIssueObject, originalWfId)
                    if(debug) log.warn("moveIssueInTxn (329)")
                } finally {
                    txn.finallyRollbackIfNotCommitted()
                }

                return mIssueOriginal.getKey()

            }

        }

    } // End public static move

    /****************************************************
    * Determines the request type from the selection.
    *
    * @param    name as String - Name of the desired request type.
    * @return   requestType as RequestType
    ****************************************************/

    static RequestType findRequestType(String name) {

        int numberOfRequestTypes   = 300 //approximate amount of customer request types on your instance the more the better)
        RequestType newRequestType = null
        RequestTypeService requestTypeService = ComponentAccessor.getOSGiComponentInstanceOfType(RequestTypeService)

        for(int limitRequest = 0; limitRequest <numberOfRequestTypes; limitRequest += 100){
            PagedRequest pagedRequest           = new SimplePagedRequest(limitRequest,limitRequest+100)
            RequestTypeQuery reqTypeQuery       = requestTypeService.newQueryBuilder().pagedRequest(pagedRequest).build()
            PagedResponse<RequestType> reqTypes = requestTypeService.getRequestTypes(ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser(), reqTypeQuery)

            if(reqTypes.results.find{ it.getName() == name }){
                newRequestType = reqTypes.results.find{it.getName() == name}
                break
            }

        }

        return newRequestType

    }

    /****************************************************
    * Determines the assigned group from the moved issue and the request type.
    *
    * @param    mIssue as MutableIssue
    * @param    requestTypeId as int
    * @return   assigned group as Group
    ****************************************************/

    static Group identifyAssignedGroup(MutableIssue mIssue, int requestTypeId) {
        
        // I have simplified it here. Because we are specifically determining the AD groups.
        // Please add your own groupname

        GroupManager groupManager = ComponentAccessor.getGroupManager()
        Group group = groupManager.getGroup("ad-group-name")
        return group

    }

} // End class
```

I would be very grateful for your help.


Answers:
author: Fabian
body: Hello [@Ram Kumar Aravindakshan _Adaptavist_](/t5/user/viewprofilepage/user-id/2988015)   
can you help me with my problem? It's a very important issue for us and unfortunately I can't get any further.

Or are there others who might have an idea?

Thank you very much for your help.


author: Ram Kumar Aravindakshan _Adaptavist_
body: Hi [@Fabian](/t5/user/viewprofilepage/user-id/4163566)

If you aim to move an issue from one project to another, why not just use [Jira Move Issue Wizard](https://confluence.atlassian.com/servicemanagementserver/moving-an-issue-939937218.html) instead of the complex code above?

Based on what condition do you want to move the issue, and what are you using to trigger the move?

And what ScriptRunner component are you using to trigger the move? Is it the ScriptRunner Console, Post-Function or Listener?

I am looking forward to your feedback and clarification.

Thank you and Kind regards,

Ram

comments: author: Fabian
body: Hello [@Ram Kumar Aravindakshan _Adaptavist_](/t5/user/viewprofilepage/user-id/2988015) ,

thank you for your fast reply. I am very happy about your assistance!  
I try to explain why we cannot use the Jira move issue wizard in this case. :-)  
We have 2 companys, which are at different locations. Unfortunately, they work differently in IT. For this reason, it was decided (under pressure from their requirements) to create two different Jira Service Management projects, some of which have different service requests and "processing groups". Now we a different use case for moving an issue:

1. another person has do work on this issue
2. another group has to work on this issue (ad-group selection)
3. wrong customer request in the same project
4. wrong customer request in "this project" --\> issue must be moved

Now jira doesnt provide good move / change features. For example:

**Case 2**: The user should only select the correct ad group --\> We cannot guarantee that the user will select the correct group. The user should only be able to select certain AD groups.

**Case 3 and 4**: We have a special routing script that selects and inserts the correct AD group when an issue is created or the issue type is changed. For many colleagues, the Move Wizard is not easy to understand. In addition, in certain constellations it does not seem to recognise the customer request type as a "mandatory field" and this ends up being empty.

As you can see, there are often cases in which a group / user agent from a different company has to move a customer issue to a different person. Or the issue has to be moved to the correct customer request in the other project.  
We have repeatedly noticed that even after training, issues are assigned incorrectly or the Jira move function is not used correctly. It has also happened quite frequently that after the Jira Move function, the issue were missing information such as the request type.  
To prevent such incorrect issues from disappearing (because some of them do not appear in the dashboard etc.), we have decided to build a custom dialog where users can only select the specific constellations and thus no information is missing for our special cases.  
I hope I have been able to describe our use case in a way that is halfway understandable.

++**How should the move function be executed?**++   
I have built a custom dialog which is called using a fragment ([custom-fragments/web-item](https://docs.adaptavist.com/sr4js/latest/features/script-fragments/custom-fragments/web-item) ). Behind this custom dialog is a JavaScript file which then addresses the corresponding REST endpoint and calls the Move function at the end.  
I know, a bit freaky :-)

I hope I was able to give you an understanding of our use case and our intended solution.  
To summarise, it can be said that the Jira move wizard does not help us at all and partly causes more problems due to the special JSM constellation and also requires "too many" actions (user unfriendly).

I hope you can help me. I'm a bit stumped here

Kind regards, Fabian


author: Ram Kumar Aravindakshan _Adaptavist_
body: Hi [@Fabian](/t5/user/viewprofilepage/user-id/4163566)

Sorry for the delay in responding. I've been very busy these past few weeks.

To proceed, in your description, you mentioned:-
> thank you for your fast reply. I am very happy about your assistance!  
> I try to explain why we cannot use the Jira move issue wizard in this case. :-)  
> We have 2 companys, which are at different locations. Unfortunately, they work differently in IT. For this reason, it was decided (under pressure from their requirements) to create two different Jira Service Management projects, some of which have different service requests and "processing groups".

Please clarify, are the issues moved between projects that reside in 2 different Jira instances, respectively or within the same instance but different projects?

If it's the former, REST Endpoint and Listener would be the way to go, i.e. depending on what will be used to trigger the issue movement. Otherwise, if it is the latter, a Listener should be enough.

Please provide the information requested, I will try to create an example.

Thank you and Kind regards,  
Ram


author: Fabian
body: Hello [@Ram Kumar Aravindakshan _Adaptavist_](/t5/user/viewprofilepage/user-id/2988015) ,

thanks for the feedback.  
The two projects are in the same instance.  
I am curious to see what I may have done wrong.

Thank you very much and kind regards,

Fabian


author: Ram Kumar Aravindakshan _Adaptavist_
body: Hi [@Fabian](/t5/user/viewprofilepage/user-id/4163566)

I have tested a very simple code in my environment, i.e. via ScriptRunner Console, and I can move the issue from one project to another without any problems.

Below is the sample working code that I have tested with:-  

```
import com.adaptavist.hapi.jira.issues.Issues

def issue = Issues.getByKey('ST-3')
def field1 = 'Sample Text Field'

def migratedIssue = Issues.create('MS', issue.issueType.name) {
    setSummary(issue.summary)
    setDescription(issue.description)
    setCustomFieldValue(field1, issue.getCustomFieldValue(field1))
    setRequestType(issue.requestType)
}

if (migratedIssue) {
    issue.delete()
}
```

Please note that the sample working code above is not 100% exact to your environment. Hence, you will need to make the required modifications.

So, for this test case, I have created a sample issue in the Sample Test (ST) JSM project and moved the issue to the Mock Sample (MS), i.e. another JSM project and the issue is migrated without any problem.

Also, for this code, I am using [ScriptRunner's HAPI](https://docs.adaptavist.com/sr4js/latest/hapi) feature to simplify the code and avoid any errors.

Please try to use the simplified approach above instead of the cluttered code you currently use and see if it works in your environment.

I am looking forward to your feedback.

Thank you and Kind regards,  
Ram

Senior Support Engineer


author: Fabian
body: Hello [@Ram Kumar Aravindakshan _Adaptavist_](/t5/user/viewprofilepage/user-id/2988015) ,

sorry for my late response.

In your example you just creat a new issue. The problem is, that i also need to copy all comments and the most important thing: the complete history is also needed.

For now i have figured out a possible solution:  

```
            mIssueOriginal = issueManager.getIssueObject(issue.getId() as Long)
            mIssueOriginal.setProject(project.genericValue)
            mIssueOriginal.setIssueTypeObject(issueType)
            Group group = identifyAssignedGroup(mIssueOriginal, requestType.getId())

            mIssueOriginal.setAssignee(null)
            mIssueOriginal.setCustomFieldValue(cfAssignedGroup, [group])
            mIssueOriginal.setCustomFieldValue(cfCRT, customerRequestType)

            //mIssueOriginal.setProjectObject(projectDestination)
            // Create map with updated issue
            Map<String,Object> updatedIssue = ["issue": mIssueOriginal] as Map<String,Object>

            // Create the updated issue in destination project
            Issue issueNew = issueManager.createIssueObject(ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser(), updatedIssue)
```

How I copy everything is realized with the issue map. So far so good.

But we have found one problem:  
The attachments are not copied. Can you help me with this or do you have a better solution?  
I have already tried various solutions, but they have not worked.

On the one hand, I took the attachment from the original issue, created a new file and attached this as an attachment to the new issue. Problem: Atlassian saves the attachments in its own format on the server and the "new" file is then not readable for the user.

I have also tried normal methods such as copyAttachment, but somehow they don't work.  

```
            attachments.each { attachment ->
                attachmentManager.copyAttachment(attachment, Users.getByName("admin"), Issues.getByKey("LWNIT-902").getKey())
}           }
```

Could it be that the reference is gone?  


Tags: adaptavist

data-center

groovy

jira-service-management

jira-service-management-data-center

jira-software

move

request-type

script

scriptrunner