Correcting record types and numbers

There's an interesting question posted on the HPE Content Manager forum.  This post will be a response to that question....

DataPort can only update an existing record if it uses the expanded number (non-compressed record number) as the unique ID for searching.  If your spreadsheet contains the record number and you match it to the expanded number field, it will first attempt to update before creating the record.  This behavior removes any ability to change the record number property via DataPort.

I mocked up the environment with the record types described and was able to generate the picture below.  The goal, I believe, is to change the pink document records (Personal) to become teal document records (Corporate).

2017-12-01_15-51-11.png

To model this environment I created a powershell script that built out these records.  This assumes you already have the 4 described record types (or you've modified this script to accommodate your environment). 

WARNING: This script creates one folder with number "17/99999" and removes any records contained inside.  It is not intended to be run in a production environment.  It is only used to prove the final solution works correctly.

#Prepare the host and load the CM SDK/Database
Clear-Host
Add-Type -Path "d:\Program Files\Hewlett Packard Enterprise\Content Manager\HP.HPTRIM.SDK.dll"
$Database = New-Object HP.HPTRIM.SDK.Database
$Database.Connect()
 
#Load the various record types
$PersonalFolderRecordType = $Database.FindTrimObjectByName([HP.HPTRIM.SDK.BaseObjectTypes]::RecordType, "Personal Files")
$PersonalDocumentRecordType = $Database.FindTrimObjectByName([HP.HPTRIM.SDK.BaseObjectTypes]::RecordType, "Personal Document")
$CorporateFolderRecordType = $Database.FindTrimObjectByName([HP.HPTRIM.SDK.BaseObjectTypes]::RecordType, "Corporate Files")
$CorporateDocumentRecordType = $Database.FindTrimObjectByName([HP.HPTRIM.SDK.BaseObjectTypes]::RecordType, "Corporate Document")
 
#Prepare the example set
$FolderNumber = "17/99999"
$ExemplarFolder = $Database.FindTrimObjectByName([HP.HPTRIM.SDK.BaseObjectTypes]::Record, $FolderNumber)
if ( $ExemplarFolder -eq $null ) {
    $ExemplarFolder = New-Object HP.HPTRIM.SDK.Record -ArgumentList $Database, $CorporateFolderRecordType
    $ExemplarFolder.LongNumber = $FolderNumber    #Assumes pattern is "YY/GGGGG"
    $ExemplarFolder.TypedTitle = "test"
    $ExemplarFolder.Save()
    Write-Host "Created folder $($ExemplarFolder)"
} else {
    #purge any existing records
    $ExistingContents = New-Object HP.HPTRIM.SDK.TrimMainObjectSearch -ArgumentList $Database,Record
    $ExistingContents.SearchString = "container:[uri:$($ExemplarFolder.Uri)]"
    foreach ( $Result in $ExistingContents ) 
    {
        Write-Host "Removing existing record $(([HP.HPTRIM.SDK.Record]$Result).Number)"
        ([HP.HPTRIM.SDK.Record]$Result).Delete()
    }
}
 
#Create 4 records within the folder, the first 2 personal and the second 2 corporate
for ( $i = 0; $i -lt 4; $i++ ) {
    $ExemplarDocument = New-Object HP.HPTRIM.SDK.Record -ArgumentList $Database, $(If ( $i -lt 2 ) { $PersonalDocumentRecordType } Else { $CorporateDocumentRecordType })
    $ExemplarDocument.DateRegistered = (get-date).AddYears(-1*$i)
    $ExemplarDocument.DateCreated = (get-date).AddYears(-1*$i)
    $ExemplarDocument.TypedTitle = "test"
    $ExemplarDocument.SetContainer($ExemplarFolder,$true)
    $ExemplarDocument.Save()
    Write-Host "Created document $($ExemplarDocument.Number)"
}

When I run the script above I get one folder with four documents.  If I run it a second time the folder will be re-used, any existing documents will be removed, and the original sample will be re-created.

2017-12-01_23-29-25.png

In a new script I craft a high-level approach to resolving the problem.  First I find all corporate folders.  Then for each folder found, I search within for any personal documents.  I'll process each one and then continue until all found documents and folders are processed.

#Prepare the host and load the CM SDK/Database
Clear-Host
Add-Type -Path "d:\Program Files\Hewlett Packard Enterprise\Content Manager\HP.HPTRIM.SDK.dll"
$Database = New-Object HP.HPTRIM.SDK.Database
$Database.Connect()
 
#Load the various record types
$PersonalFolderRecordType = $Database.FindTrimObjectByName([HP.HPTRIM.SDK.BaseObjectTypes]::RecordType, "Personal Files")
$PersonalDocumentRecordType = $Database.FindTrimObjectByName([HP.HPTRIM.SDK.BaseObjectTypes]::RecordType, "Personal Document")
$CorporateFolderRecordType = $Database.FindTrimObjectByName([HP.HPTRIM.SDK.BaseObjectTypes]::RecordType, "Corporate Files")
$CorporateDocumentRecordType = $Database.FindTrimObjectByName([HP.HPTRIM.SDK.BaseObjectTypes]::RecordType, "Corporate Document")
 
#Find all corporate folders
$CorporateFolders = New-Object HP.HPTRIM.SDK.TrimMainObjectSearch -ArgumentList $Database, Record
$CorporateFolders.SearchString = "type:[uri:$($CorporateFolderRecordType.Uri)]"
foreach ( $CorporateFolder in $CorporateFolders ) {
    #Find and process all personal documents
    $PersonalDocuments = New-Object HP.HPTRIM.SDK.TrimMainObjectSearch -ArgumentList $Database, Record
    $PersonalDocuments.SearchString = "container:$($CorporateFolder.Uri) type:[uri:$($PersonalDocumentRecordType.Uri)]"
    Write-Host "Folder $($CorporateFolder.Number) has $($PersonalDocuments.Count) documents to fix"
    foreach ( $PersonalDocument in $PersonalDocuments ) {
        #Change record type and number
        Write-Host "Fixing Document $($PersonalDocument.Number)"
    }
}

Running this script gives me the following results...

2017-12-01_16-06-01.png

Sweet!  Now I just need some logic that actually fixes the record.  First I'll have it change the record type.  Then I'll figure out the appropriate prefix for the record's new record number (D17#, D16#, D11#, whatever).  With that in-hand I'll search for all records starting with that prefix, sorted to give me the last number first.  I grab that number and add one.  If there are no records from that year (which is the case in my scenario from the powershell script above) then I use 1 as the starting point.  

$PersonalDocument.RecordType = $CorporateDocumentRecordType
        #Figure out the correct number
        $Prefix = "D$($PersonalDocument.DateRegistered.Year.ToString().Substring(2))"
        $LastRecords = New-Object HP.HPTRIM.SDK.TrimMainObjectSearch -ArgumentList $database, Record
        $LastRecords.SearchString = "number:$($prefix)*"
        $LastRecords.SetSortString("number-")
        if ( $LastRecords.Count -eq 0 ) {
            $RecordNumber = "$($prefix)#1"
        } else {
            $Enumerator = $LastRecords.GetEnumerator()
            $Enumerator.MoveNext()
            $Record = [HP.HPTRIM.SDK.Record]$Enumerator.Current
            $LastNumber = $Record.Number.Replace("$($prefix)#","")
            $RecordNumber = "$($prefix)#$([convert]::ToInt32($LastNumber)+1)"
        }
        $PersonalDocument.LongNumber = $RecordNumber
        $PersonalDocument.Save()

After I run this I can see the changes within the client. Success!

2017-12-01_16-25-29.png

It's worth pointing out that in the newer versions of CM you can prevent users from storing personal documents into corporate containers.  

The complete script that fixes all records in the dataset is as follows:

#Prepare the host and load the CM SDK/Database
Clear-Host
Add-Type -Path "d:\Program Files\Hewlett Packard Enterprise\Content Manager\HP.HPTRIM.SDK.dll"
$Database = New-Object HP.HPTRIM.SDK.Database
$Database.Connect()
 
#Load the various record types
$PersonalFolderRecordType = $Database.FindTrimObjectByName([HP.HPTRIM.SDK.BaseObjectTypes]::RecordType, "Personal Files")
$PersonalDocumentRecordType = $Database.FindTrimObjectByName([HP.HPTRIM.SDK.BaseObjectTypes]::RecordType, "Personal Document")
$CorporateFolderRecordType = $Database.FindTrimObjectByName([HP.HPTRIM.SDK.BaseObjectTypes]::RecordType, "Corporate Files")
$CorporateDocumentRecordType = $Database.FindTrimObjectByName([HP.HPTRIM.SDK.BaseObjectTypes]::RecordType, "Corporate Document")
 
#Find all corporate folders
$CorporateFolders = New-Object HP.HPTRIM.SDK.TrimMainObjectSearch -ArgumentList $Database, Record
$CorporateFolders.SearchString = "type:[uri:$($CorporateFolderRecordType.Uri)]"
foreach ( $CorporateFolder in $CorporateFolders ) {
    #Find and process all personal documents
    $PersonalDocuments = New-Object HP.HPTRIM.SDK.TrimMainObjectSearch -ArgumentList $Database, Record
    $PersonalDocuments.SearchString = "container:$($CorporateFolder.Uri) type:[uri:$($PersonalDocumentRecordType.Uri)]"
    Write-Host "Folder $($CorporateFolder.Number) has $($PersonalDocuments.Count) documents to fix"
    foreach ( $PersonalDocument in $PersonalDocuments ) {
        #Change record type and number
        Write-Host "Fixing Document $($PersonalDocument.Number)"
        $PersonalDocument.RecordType = $CorporateDocumentRecordType
        #Figure out the correct number
        $Prefix = "D$($PersonalDocument.DateRegistered.Year.ToString().Substring(2))"
        $LastRecords = New-Object HP.HPTRIM.SDK.TrimMainObjectSearch -ArgumentList $database, Record
        $LastRecords.SearchString = "number:$($prefix)*"
        $LastRecords.SetSortString("number-")
        if ( $LastRecords.Count -eq 0 ) {
            $RecordNumber = "$($prefix)#1"
        } else {
            $Enumerator = $LastRecords.GetEnumerator()
            $Enumerator.MoveNext()
            $Record = [HP.HPTRIM.SDK.Record]$Enumerator.Current
            $LastNumber = $Record.Number.Replace("$($prefix)#","")
            $RecordNumber = "$($prefix)#$([convert]::ToInt32($LastNumber)+1)"
        }
        $PersonalDocument.LongNumber = $RecordNumber
        $PersonalDocument.Save()
    }
}