Skip to main content
Question

Portfolios License Archiving Process

  • December 11, 2025
  • 2 replies
  • 38 views

Forum|alt.badge.img+1

We are evaluating the most cost effective way to manage our licenses so are currently defining an archive process based mostly on lack of use.  I’d also like to downgrade a Portfolio-R/W license to a View Only license if I could tell if the user has not made any data updates in the past three months or so.   Does anyone know a way to do this?

We are also considering alternative solutions like report subscriptions to keep licensing costs down and wonder if others have pursued this or any other solutions?

Thanks!

2 replies

Forum|alt.badge.img
  • Bronze Active Participant
  • January 6, 2026

FYI: There is a tile called “Users” that shows last login.

I wrote a quick script (attached to tile) that shows last users where last login was > x days. We also check for users where maybe the resource got disabled but the user didn’t. I’ve not done data update checks, but it sounds doable using a custom script. 

We are doing some reporting outside Portfolios using API’s. 


Forum|alt.badge.img
  • February 12, 2026

Licensing can be controlled with Planview’s SOAP API.    You can change this dynamically based on user needs.   

 

Sample Code.

def update_license(df, hostName, PVCert):  # Need a Dataframe with "License","User ID","Name"    wsdl = "https://" + hostName + "/Planview/services/UserService.svc?wsdl"    client = Client(wsdl=wsdl, port_name="BasicHttpBinding_IUserService")    headers = {"Content-type": "text/xml",               "Cookie": PVCert}    client.transport.session.headers.update(headers)    count = 0    for idx, row in df.iterrows():        try:            results = client.service.Update(dtos=dict(UserDto={"UserKey": "key://3/" + str(row.loc["User ID"]),                                                               "RoleKey": "key://2/$Role/" + "923"   ←   You need the license SQL key Number                                                               }),                                            )            if results["Failures"] is None:                logger.info("%s was updated successfully with new license code",row.loc["Name"])                count += 1            else:                logger.info("--------%s failed to be updated with New License Code --------",str(row.loc["Name"]))        except Exception as e:            logger.exception(                "Failed to update %s in Planview (attempted New License Code %s and resource role %s)",                row.loc["Name"],                row.loc["New License Code"],                row.loc["resource_role"]            )    return count