cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Showing results for 
Search instead for 
Did you mean: 

Community Tip - Visit the PTCooler (the community lounge) to get to know your fellow community members and check out some of Dale's Friday Humor posts! X

Translate the entire conversation x

How to create WTPartReferenceLink using Windchill REST Services in Windchill PDMLink

CB_12625553
12-Amethyst

How to create WTPartReferenceLink using Windchill REST Services in Windchill PDMLink

Version: Windchill 12.1

 

Use Case: Using Windchill REST Services to creating a Referenced By link between a Part and a Document


Description:

I have followed this guide to successfully create a customization to create a WTPartDescribeLink using WRS: https://www.ptc.com/en/support/article/CS285572?source=search

 

The guide also mentions how to apply the same steps for WTPartReferenceLink, but I am receiving an error and would appreciate some guidance.

 

URL: POST https://mywindchill.portal.ptc.io:8443/Windchill/servlet/odata/ProdMgmt/Parts('OR:wt.part.WTPart:226444')/References

 

{
    "ReferencedBy@odata.bind": "PTC.ProdMgmt.CustomCompanyParts('OR:wt.part.WTPart:226444')",
    "References@odata.bind": "PTC.DocMgmt.Documents('OR:wt.doc.WTDocument:226427')"
}

 

The error:

{
    "error": {
        "code": null,
        "message": "Unable to create entity - Cannot cast wt.doc.WTDocumentMaster to wt.vc.wip.Workable"
    }
}

 

I would really appreciate some help resolving this issue.

 

Attached is my code for this, which was uploaded to "/codebase/rest/ptc/domain/ProdMgmt/v7/entity". I took the working copy used in the tutorial for PartDescribesLink and modified it for this. I must be missing something!

 

Part of my struggle is that I do not know how to troubleshoot this. I attempted to add logging statements following this guide like so:

 

var mylogger = log4javascript.getLogger("PTC.mypackage");
mylogger.trace("...");

 

However, this returns the error "log4javascript is not defined". I suspect there's more to do than what the guide says. I feel like I'm flying blind, I am editing some code with Notepad++ and having to restart the server takes 15 minutes anytime I want to test something.

 

Perhaps this is an easy fix, but I looked at other related topics for my issue, but no resolution was posted, so any additional help would be very much appreciated!

ACCEPTED SOLUTION

Accepted Solutions

Here is the solution code that you will need:

 

PartReferenceLink.js

function getRelatedEntityCollection(navigationData) {
    var HashMap = Java.type('java.util.HashMap');
    var WTArrayList = Java.type('wt.fc.collections.WTArrayList');
    var Collections = Java.type('java.util.Collections');
    var VersionControlHelper = Java.type("wt.vc.VersionControlHelper");
    var SessionHelper = Java.type('wt.session.SessionHelper');
    var AccessControlHelper = Java.type('wt.access.AccessControlHelper');
    var AccessPermission = Java.type('wt.access.AccessPermission');
    var ODataApplicationException = Java.type('org.apache.olingo.server.api.ODataApplicationException');
    var HttpStatusCode = Java.type('org.apache.olingo.commons.api.http.HttpStatusCode');
    var WTMessage = Java.type('wt.util.WTMessage');
    var AccessResource = Java.type('wt.access.accessResource');

    var targetName = navigationData.getTargetSetName();
    var sourceLinks = new WTArrayList(navigationData.getSourceObjects());
    var user = SessionHelper.getPrincipal();

    if ("References".equals(targetName)) {
        var map = new HashMap();

        try {
            SessionHelper.manager.setAdministrator();

            for (var i = 0, l = sourceLinks.size(); i < l; i++) {
                var sourceLink = sourceLinks.getPersistable(i);
                var document = sourceLink.getRoleBObject();

                var latestDocument = VersionControlHelper.service.getLatestIteration(document, false);
                var hasAccessToReferencedBy = AccessControlHelper.manager.hasAccess(user, latestDocument, AccessPermission.READ);

                if (hasAccessToReferencedBy) {
                    map.put(sourceLink, Collections.singletonList(latestDocument));
                } else {
                    var errorMsg = WTMessage.getLocalizedMessage(AccessResource.class.getName(),
                            AccessResource.SECURED_INFORMATION, null, navigationData.getLocale());
                    throw new ODataApplicationException(errorMsg, HttpStatusCode.FORBIDDEN.getStatusCode(),
                            navigationData.getLocale());
                }
            }
        } finally {
            // de-elevate administrator
            SessionHelper.manager.setPrincipal(user.getName());
        }
        return map;
    } 

    return new HashMap();
}

function processAdditionalAttributes(entity, object, data, processor) {
    var ArrayList = Java.type('java.util.ArrayList');
    var WTPart = Java.type('wt.part.WTPart');
    var WTDocument = Java.type('wt.doc.WTDocument');
    var NmOid = Java.type('com.ptc.netmarkets.model.NmOid');
    var EntityIDUtils = Java.type('com.ptc.odata.core.entity.processor.EntityIDUtils');
    var AttributeData = Java.type('com.ptc.odata.windchill.entity.processor.AttributeData');
    var EntityOperation = Java.type('com.ptc.odata.core.entity.processor.EntityOperation');
    var ODataApplicationException = Java.type('org.apache.olingo.server.api.ODataApplicationException');
    var HttpStatusCode = Java.type('org.apache.olingo.commons.api.http.HttpStatusCode');
    var WTMessage = Java.type('wt.util.WTMessage');
    var ProdMgmtRB = Java.type('com.ptc.odata.windchill.part.entity.ProdMgmtRB');
    var AssociationConstraintHelper = Java.type('com.ptc.core.meta.type.mgmt.server.impl.association.AssociationConstraintHelper');
    var ClientTypedUtility = Java.type('wt.type.ClientTypedUtility');

    var attributes = new ArrayList(2);
    if (data.getOperation() == EntityOperation.CREATE) {
        var referencesIdStr = EntityIDUtils.getInstance().getBindingLinkId(data, entity, "References");
        var referencesOid = referencesIdStr != null ? NmOid.newNmOid(referencesIdStr) : null;
        if (referencesOid != null && referencesOid.isA(WTDocument.class)) {
            attributes.add(new AttributeData("references", referencesOid.getRefObject().getMaster()));
        }

        var referencePart = data.getSourceObjects().iterator().next();
        attributes.add(new AttributeData("referencedBy", referencePart));
        
        // make sure our objects are valid for the link constraints
        if (!AssociationConstraintHelper.service.isValidAssociation(
                referencePart,
                ClientTypedUtility.getTypeIdentifier("wt.part.WTPartReferenceLink"),
                referencesOid.getRefObject())) {
            var errorMsg = "Invalid types for link, does not meet relationship constraints";
            throw new ODataApplicationException(errorMsg, HttpStatusCode.FORBIDDEN.getStatusCode(), data.getLocale())
        }
    }

    return attributes;
}


function toObjects(entities, processorData) {
    var ObjectAttributeProcessor = Java.type('com.ptc.odata.windchill.entity.processor.ObjectAttributeProcessor');
    var ODataApplicationException = Java.type('org.apache.olingo.server.api.ODataApplicationException');
    var HttpStatusCode = Java.type('org.apache.olingo.commons.api.http.HttpStatusCode');
    var WTMessage = Java.type('wt.util.WTMessage');
    var ProdMgmtRB = Java.type('com.ptc.odata.windchill.part.entity.ProdMgmtRB');

    // create the object the normal way
    var entityToObjectMap = ObjectAttributeProcessor.newInstance().toObjects(entities, processorData);


    return entityToObjectMap;
}

function checkForCheckout(newObject, createdEntity, processorData) {
    var ODataApplicationException = Java.type('org.apache.olingo.server.api.ODataApplicationException');
    var HttpStatusCode = Java.type('org.apache.olingo.commons.api.http.HttpStatusCode');
    var WTMessage = Java.type('wt.util.WTMessage');
    var ProdMgmtRB = Java.type('com.ptc.odata.windchill.part.entity.ProdMgmtRB');
    var WorkInProgressHelper = Java.type('wt.vc.wip.WorkInProgressHelper');
    var SessionHelper = Java.type('wt.session.SessionHelper');
    
    var referencePart = newObject.getReferencedBy();
    
    // check that the part is checked out and it is checked out to us
    if (!WorkInProgressHelper.isCheckedOut(referencePart, SessionHelper.getPrincipal()) ||
        !WorkInProgressHelper.isReservedWorkingCopy(referencePart)) {
        var errorMsg = "Part must be checked out and be the working copy to add, update, or delete WTPartReferenceLink";
        throw new ODataApplicationException(errorMsg, HttpStatusCode.FORBIDDEN.getStatusCode(), processorData
                .getLocale())
    } else {
        processorData.setContinueProcessing(true);
        return newObject;
    }
}

function storeNewObject(objectToSave, entity, processorData) {
    return checkForCheckout(objectToSave, entity, processorData);
}

function saveObject(objectToSave, entity, processorData) {
    return checkForCheckout(objectToSave, entity, processorData);
}

 

PartReferenceLink.json

{
    "name":"PartReferenceLink",
    "collectionName":"PartReferenceLinks",
    "type":"wcType",
    "wcType":"wt.part.WTPartReferenceLink",
    "description":"Part reference link",
    "operations":"CREATE,UPDATE,DELETE",
    "includeInServiceDocument":false,
    "defaultForWcType":true,
    "parent":{
        "name":"WindchillEntities"
    }, 
    "attributes":[],
    "inherits":[
        {
            "name": "softattributable",
            "version": "5"
        }
    ],    
    "navigations":[
        {
         "name":"ReferencedBy",
         "target":"Parts",
         "type":"Part",
         "isCollection":false,
         "partner":"",
         "internalNameForDisplayName":"referencedBy"  
        },
        {
         "name":"References",
         "target":"PTC.DocMgmt.Documents",
         "type":"PTC.DocMgmt.Document",
         "isCollection":false,
         "partner":"", 
         "internalNameForDisplayName":"references" 
        }
    ]
}

 

Now you can hit this endpoint:

POST /ProdMgmt/Parts('{PartId}')/References

{
    "ReferencedBy@odata.bind": "PTC.ProdMgmt.MyCustomPartEntity('{PartId}')",
    "References@odata.bind": "PTC.DocMgmt.Documents('{DocumentId}')"
}

 

You can add it to your CCD package by putting the files in this big nested folder structure:

/my_CustomModule/main/resources/rest/custom/domain/ProdMgmt/v7/entity/

 

View solution in original post

10 REPLIES 10

I didn’t try the code but did you change the preference Part To Document Association Logic.

 

CB_12625553
12-Amethyst
(To:rhart)

I have not, where would I find that?

In Windchill > Utilities > Preferences, I think it needs to be set to Yes

 

Here's a snippet of log4j logging

 

package ext.MA.tools;

import org.apache.logging.log4j.Logger;
import wt.log4j.LogR;

public class ConversionHelper {

	private static final String CLASSNAME = ConversionHelper.class.getName();

	private static final Logger LOGGER = LogR.getLoggerInternal(CLASSNAME);
	
    public static Boolean getBooleanFromBLOB(final Object o, final String s) throws WTException {
		
		LOGGER.debug("Workflow variable '" + s + "' was not found in ProcessData.");
		
	}
}

 

In js it would be something like this,

//include the library, e.g. add it via cdn 

<script src="https://cdnjs.cloudflare.com/ajax/libs/log4javascript/1.4.13/log4javascript.js"></script>

const logger = log4javascript.getLogger("ConversionHelper");

// Add a browser console appender
logger.addAppender(new log4javascript.BrowserConsoleAppender());

// Set the level to debug
logger.setLevel(log4javascript.Level.DEBUG);

logger.debug(`Workflow variable '${s}' was not found in ProcessData.`);

 

Hi @CB_12625553,

 

I wanted to see if you got the help you needed.

If so, please mark the appropriate reply as the Accepted Solution. It will help other members who may have the same question.
Of course, if you have more to share on your issue, please pursue the conversation. 

 

Thanks,
Anurag 

Logging in WRS extensions (javascript files) can be done as follows:

var LogManager = Java.type('wt.log4j.LogManager');
var Logger = Java.type('wt.log4j.Logger');
var logger = LogManager.getLogger("rest.custom.domain.mycustompackage");
logger.debug("CUSTOMIZATION START ...");

 

With Windchill REST Services 12.1.2.14, the Product Management v7 domain:

◦ Introduced the ability to create a part describe link between an existing part and a document.
◦ Introduced the ability to modify an existing part describe link.
◦ Introduced the ability to delete an existing part describe link.

See https://support.ptc.com/help/windchill/plus/r12.1.2.0/en/#page/Windchill_Help_Center/NewandChanged_WRS/wrs_ProdMgmt_domain_12_1_2_14.html#

 

Here is the solution code that you will need:

 

PartReferenceLink.js

function getRelatedEntityCollection(navigationData) {
    var HashMap = Java.type('java.util.HashMap');
    var WTArrayList = Java.type('wt.fc.collections.WTArrayList');
    var Collections = Java.type('java.util.Collections');
    var VersionControlHelper = Java.type("wt.vc.VersionControlHelper");
    var SessionHelper = Java.type('wt.session.SessionHelper');
    var AccessControlHelper = Java.type('wt.access.AccessControlHelper');
    var AccessPermission = Java.type('wt.access.AccessPermission');
    var ODataApplicationException = Java.type('org.apache.olingo.server.api.ODataApplicationException');
    var HttpStatusCode = Java.type('org.apache.olingo.commons.api.http.HttpStatusCode');
    var WTMessage = Java.type('wt.util.WTMessage');
    var AccessResource = Java.type('wt.access.accessResource');

    var targetName = navigationData.getTargetSetName();
    var sourceLinks = new WTArrayList(navigationData.getSourceObjects());
    var user = SessionHelper.getPrincipal();

    if ("References".equals(targetName)) {
        var map = new HashMap();

        try {
            SessionHelper.manager.setAdministrator();

            for (var i = 0, l = sourceLinks.size(); i < l; i++) {
                var sourceLink = sourceLinks.getPersistable(i);
                var document = sourceLink.getRoleBObject();

                var latestDocument = VersionControlHelper.service.getLatestIteration(document, false);
                var hasAccessToReferencedBy = AccessControlHelper.manager.hasAccess(user, latestDocument, AccessPermission.READ);

                if (hasAccessToReferencedBy) {
                    map.put(sourceLink, Collections.singletonList(latestDocument));
                } else {
                    var errorMsg = WTMessage.getLocalizedMessage(AccessResource.class.getName(),
                            AccessResource.SECURED_INFORMATION, null, navigationData.getLocale());
                    throw new ODataApplicationException(errorMsg, HttpStatusCode.FORBIDDEN.getStatusCode(),
                            navigationData.getLocale());
                }
            }
        } finally {
            // de-elevate administrator
            SessionHelper.manager.setPrincipal(user.getName());
        }
        return map;
    } 

    return new HashMap();
}

function processAdditionalAttributes(entity, object, data, processor) {
    var ArrayList = Java.type('java.util.ArrayList');
    var WTPart = Java.type('wt.part.WTPart');
    var WTDocument = Java.type('wt.doc.WTDocument');
    var NmOid = Java.type('com.ptc.netmarkets.model.NmOid');
    var EntityIDUtils = Java.type('com.ptc.odata.core.entity.processor.EntityIDUtils');
    var AttributeData = Java.type('com.ptc.odata.windchill.entity.processor.AttributeData');
    var EntityOperation = Java.type('com.ptc.odata.core.entity.processor.EntityOperation');
    var ODataApplicationException = Java.type('org.apache.olingo.server.api.ODataApplicationException');
    var HttpStatusCode = Java.type('org.apache.olingo.commons.api.http.HttpStatusCode');
    var WTMessage = Java.type('wt.util.WTMessage');
    var ProdMgmtRB = Java.type('com.ptc.odata.windchill.part.entity.ProdMgmtRB');
    var AssociationConstraintHelper = Java.type('com.ptc.core.meta.type.mgmt.server.impl.association.AssociationConstraintHelper');
    var ClientTypedUtility = Java.type('wt.type.ClientTypedUtility');

    var attributes = new ArrayList(2);
    if (data.getOperation() == EntityOperation.CREATE) {
        var referencesIdStr = EntityIDUtils.getInstance().getBindingLinkId(data, entity, "References");
        var referencesOid = referencesIdStr != null ? NmOid.newNmOid(referencesIdStr) : null;
        if (referencesOid != null && referencesOid.isA(WTDocument.class)) {
            attributes.add(new AttributeData("references", referencesOid.getRefObject().getMaster()));
        }

        var referencePart = data.getSourceObjects().iterator().next();
        attributes.add(new AttributeData("referencedBy", referencePart));
        
        // make sure our objects are valid for the link constraints
        if (!AssociationConstraintHelper.service.isValidAssociation(
                referencePart,
                ClientTypedUtility.getTypeIdentifier("wt.part.WTPartReferenceLink"),
                referencesOid.getRefObject())) {
            var errorMsg = "Invalid types for link, does not meet relationship constraints";
            throw new ODataApplicationException(errorMsg, HttpStatusCode.FORBIDDEN.getStatusCode(), data.getLocale())
        }
    }

    return attributes;
}


function toObjects(entities, processorData) {
    var ObjectAttributeProcessor = Java.type('com.ptc.odata.windchill.entity.processor.ObjectAttributeProcessor');
    var ODataApplicationException = Java.type('org.apache.olingo.server.api.ODataApplicationException');
    var HttpStatusCode = Java.type('org.apache.olingo.commons.api.http.HttpStatusCode');
    var WTMessage = Java.type('wt.util.WTMessage');
    var ProdMgmtRB = Java.type('com.ptc.odata.windchill.part.entity.ProdMgmtRB');

    // create the object the normal way
    var entityToObjectMap = ObjectAttributeProcessor.newInstance().toObjects(entities, processorData);


    return entityToObjectMap;
}

function checkForCheckout(newObject, createdEntity, processorData) {
    var ODataApplicationException = Java.type('org.apache.olingo.server.api.ODataApplicationException');
    var HttpStatusCode = Java.type('org.apache.olingo.commons.api.http.HttpStatusCode');
    var WTMessage = Java.type('wt.util.WTMessage');
    var ProdMgmtRB = Java.type('com.ptc.odata.windchill.part.entity.ProdMgmtRB');
    var WorkInProgressHelper = Java.type('wt.vc.wip.WorkInProgressHelper');
    var SessionHelper = Java.type('wt.session.SessionHelper');
    
    var referencePart = newObject.getReferencedBy();
    
    // check that the part is checked out and it is checked out to us
    if (!WorkInProgressHelper.isCheckedOut(referencePart, SessionHelper.getPrincipal()) ||
        !WorkInProgressHelper.isReservedWorkingCopy(referencePart)) {
        var errorMsg = "Part must be checked out and be the working copy to add, update, or delete WTPartReferenceLink";
        throw new ODataApplicationException(errorMsg, HttpStatusCode.FORBIDDEN.getStatusCode(), processorData
                .getLocale())
    } else {
        processorData.setContinueProcessing(true);
        return newObject;
    }
}

function storeNewObject(objectToSave, entity, processorData) {
    return checkForCheckout(objectToSave, entity, processorData);
}

function saveObject(objectToSave, entity, processorData) {
    return checkForCheckout(objectToSave, entity, processorData);
}

 

PartReferenceLink.json

{
    "name":"PartReferenceLink",
    "collectionName":"PartReferenceLinks",
    "type":"wcType",
    "wcType":"wt.part.WTPartReferenceLink",
    "description":"Part reference link",
    "operations":"CREATE,UPDATE,DELETE",
    "includeInServiceDocument":false,
    "defaultForWcType":true,
    "parent":{
        "name":"WindchillEntities"
    }, 
    "attributes":[],
    "inherits":[
        {
            "name": "softattributable",
            "version": "5"
        }
    ],    
    "navigations":[
        {
         "name":"ReferencedBy",
         "target":"Parts",
         "type":"Part",
         "isCollection":false,
         "partner":"",
         "internalNameForDisplayName":"referencedBy"  
        },
        {
         "name":"References",
         "target":"PTC.DocMgmt.Documents",
         "type":"PTC.DocMgmt.Document",
         "isCollection":false,
         "partner":"", 
         "internalNameForDisplayName":"references" 
        }
    ]
}

 

Now you can hit this endpoint:

POST /ProdMgmt/Parts('{PartId}')/References

{
    "ReferencedBy@odata.bind": "PTC.ProdMgmt.MyCustomPartEntity('{PartId}')",
    "References@odata.bind": "PTC.DocMgmt.Documents('{DocumentId}')"
}

 

You can add it to your CCD package by putting the files in this big nested folder structure:

/my_CustomModule/main/resources/rest/custom/domain/ProdMgmt/v7/entity/

 

Hi, I am trying to implement this. After putting the files, I am trying to make the request. I checked out the Part before making the request. Even with checked out part I am getting the error code 403 with following response body.

"

{  "error": {    "code": null,    "message": "Part must be checked out and be the working copy to add, update, or delete WTPartReferenceLink"
  }
}

"

Also I used the request body as following 

"

{
"ReferencedBy@odata.bind": "PTC.ProdMgmt.MechanicalPart('OR:wt.part.WTPart:349320081')",
"References@odata.bind": "PTC.DocMgmt.Documents('OR:wt.doc.WTDocument:349319669')"
}

"

Is there anything similar you have faced. I would be grateful for any help or suggestion. Thanks 

MalteSiefkes
13-Aquamarine
(To:Alex_337)

Hello,  instead of debugging above (are you using the OID of the working copy?), you might upgrade to the latest Windchill version. The functionality discussed here is now available in standard Windchill:

With Windchill REST Services 13.1.1.0, the Product Management v7 domain has been updated:

◦ Introduced the ability to create a part reference link between an existing part and a reference document.

◦ Introduced the ability to modify an existing part reference link.

◦ Introduced the ability to delete an existing part reference link.

See https://support.ptc.com/help/windchill/r13.1.1.0/en/#page/Windchill_Help_Center/NewandChanged_WRS/wrs_ProdMgmt_domain_13_1_10.html#

Hello @MalteSiefkes, Yes, I am using the OID of the working copy and still getting the response

"

"Part must be checked out and be the working copy to add, update, or delete WTPartReferenceLink"

".  

we are on Windchill REST Services 13.0.2.3, and in this release, this functionality is not available; therefore, it should be implemented. For upgrading to Windchill latest version I am not sure what the consequences will be. Therefore, I am trying to implement this into the current version, and any hints or help in this regard will be appreciated. Thanks

Hi @MalteSiefkes, thanks for the suggestion. I spent some time with the script above and successfully implemented the functionality.

Announcements
Top Tags