Add populate_tablebox.ps1 template + SKILL.md Step 6b (AddField pattern)
This commit is contained in:
parent
a3cb35f078
commit
753a9bd80b
2 changed files with 127 additions and 0 deletions
30
SKILL.md
30
SKILL.md
|
|
@ -109,6 +109,36 @@ Use the template `templates/Reload_wrapped.bat`. Substitute:
|
|||
Warn user: all `git commit` commands use `--allow-empty` so they never
|
||||
fail even when nothing changed.
|
||||
|
||||
## Step 6b: Populating tableboxes programmatically
|
||||
|
||||
QlikView's COM `TableBoxProperties.Fields.Add()` is BROKEN on modern QV
|
||||
(returns "null-valued expression"). The working pattern is
|
||||
`$tableBox.AddField($fieldName)` — called directly on the SheetObject, NOT on
|
||||
its properties. Use template `templates/populate_tablebox.ps1`:
|
||||
|
||||
```powershell
|
||||
powershell -File populate_tablebox.ps1 `
|
||||
-QvwPath "C:\path\to\app.qvw" `
|
||||
-SheetName "Cash Flow" `
|
||||
-Fields @("%ProdPODateKey","cf_category","cf_payment_date","cf_amount_ron")
|
||||
```
|
||||
|
||||
The script creates the sheet if missing (case-insensitive substring match on
|
||||
name), creates a fresh TableBox on it, and calls AddField per entry.
|
||||
Fields that don't exist in the data model fail silently — pass
|
||||
`-ReplaceExisting $true` to purge existing TBs on the sheet first (removes
|
||||
duplicates when re-running with corrected field lists).
|
||||
|
||||
Common gotchas:
|
||||
- Field must exist in a RESIDENT table at save time. `Dim*` / `Fact*` tables
|
||||
often get dropped in a final-schema-reduction step — their fields won't
|
||||
populate. Check the `1700.ExportCSV.qvs`-style DROP list.
|
||||
- `%CompoundKey`-style field names (`%ProdPODateKey`, `%SupplierPOKey`) work
|
||||
fine — the `%` prefix is literal.
|
||||
- For Dim-only fields that DO survive (e.g. after renaming a drop list),
|
||||
run the `qvw-extract-script` skill first to audit what's actually in the
|
||||
final QVW schema.
|
||||
|
||||
## Step 7: Changing the script include path (if needed)
|
||||
|
||||
If the QVW's embedded script points at an old `9.QVSvN` folder and needs
|
||||
|
|
|
|||
97
templates/populate_tablebox.ps1
Normal file
97
templates/populate_tablebox.ps1
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
# populate_tablebox.ps1 — create a new sheet with a populated TableBox on a QVW.
|
||||
#
|
||||
# IMPORTANT: use TableBox.AddField($fieldName) — NOT TableBoxProperties.Fields.Add()
|
||||
# (which fails on modern QV Document COM with "null-valued expression").
|
||||
#
|
||||
# Usage:
|
||||
# powershell -File populate_tablebox.ps1 `
|
||||
# -QvwPath "C:\path\to\app.qvw" `
|
||||
# -SheetName "Cash Flow (v9)" `
|
||||
# -Fields @("%ProdPODateKey","cf_category","cf_payment_date","cf_amount_ron","ext_supplier_code")
|
||||
#
|
||||
# The sheet is CREATED if it doesn't match `$SheetName` (case-insensitive substring).
|
||||
# A fresh TableBox is always created on the sheet. Existing TBs are NOT deleted —
|
||||
# pass -ReplaceExisting $true to remove old TBs on the sheet first.
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$QvwPath,
|
||||
[Parameter(Mandatory=$true)][string]$SheetName,
|
||||
[Parameter(Mandatory=$true)][string[]]$Fields,
|
||||
[bool]$ReplaceExisting = $false
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Continue"
|
||||
if (-not (Test-Path $QvwPath)) { throw "QVW not found: $QvwPath" }
|
||||
|
||||
$qv = New-Object -ComObject QlikTech.QlikView
|
||||
$doc = $qv.OpenDoc($QvwPath, "", "")
|
||||
if (-not $doc) { throw "OpenDoc returned null for $QvwPath" }
|
||||
Start-Sleep -Seconds 2
|
||||
|
||||
# Find or create the sheet
|
||||
$targetSheet = $null
|
||||
$targetSheetId = $null
|
||||
for ($i=0; $i -lt $doc.NoOfSheets(); $i++) {
|
||||
$s = $doc.GetSheet($i)
|
||||
$sp = $s.GetProperties()
|
||||
$nm = $null
|
||||
try { $nm = $sp.Name } catch { try { $nm = $sp.Name.v } catch {} }
|
||||
if ($nm -and ($nm -like "*$SheetName*" -or $nm -eq $SheetName)) {
|
||||
$targetSheet = $s
|
||||
$targetSheetId = $sp.SheetId
|
||||
Write-Host "sheet exists: $nm (id=$targetSheetId)"
|
||||
break
|
||||
}
|
||||
}
|
||||
if (-not $targetSheet) {
|
||||
$targetSheet = $doc.CreateSheet()
|
||||
$sp = $targetSheet.GetProperties()
|
||||
try { $sp.Name = $SheetName } catch { try { $sp.Name.v = $SheetName } catch {} }
|
||||
$targetSheet.SetProperties($sp)
|
||||
$targetSheetId = $targetSheet.GetProperties().SheetId
|
||||
Write-Host "sheet created: $SheetName (id=$targetSheetId)"
|
||||
}
|
||||
|
||||
# Optionally remove existing TBs on the sheet (reads -prj XML to find them)
|
||||
if ($ReplaceExisting) {
|
||||
$prjPath = [System.IO.Path]::GetDirectoryName($QvwPath) + "\" + [System.IO.Path]::GetFileNameWithoutExtension($QvwPath) + "-prj"
|
||||
$prjXml = Join-Path $prjPath "QlikViewProject.xml"
|
||||
if (Test-Path $prjXml) {
|
||||
$xml = [xml](Get-Content $prjXml)
|
||||
$sheetNode = $xml.PrjQlikViewProject.SHEETS.PrjSheetProperties | Where-Object { $_.SheetId -eq "Document\$targetSheetId" }
|
||||
if ($sheetNode) {
|
||||
foreach ($ch in $sheetNode.ChildObjects.PrjFrameParentDef) {
|
||||
if ($ch.ObjectId -like "Document\TB*") {
|
||||
$tbId = $ch.ObjectId.Replace("Document\","")
|
||||
try {
|
||||
$tb = $doc.GetSheetObject("Document\$tbId")
|
||||
if ($tb) { $tb.Close() | Out-Null; Write-Host " - removed $tbId" }
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Create fresh tablebox
|
||||
$tb = $targetSheet.CreateTableBox()
|
||||
Start-Sleep -Milliseconds 400
|
||||
|
||||
# Populate via AddField (THIS is the working pattern)
|
||||
$added = 0; $failed = @()
|
||||
foreach ($f in $Fields) {
|
||||
try {
|
||||
$tb.AddField($f) | Out-Null
|
||||
$added++
|
||||
} catch {
|
||||
$failed += $f
|
||||
}
|
||||
}
|
||||
Write-Host "populated $added/$($Fields.Count) fields"
|
||||
if ($failed.Count) { Write-Host "missed: $($failed -join ', ')" }
|
||||
|
||||
$doc.Save()
|
||||
Start-Sleep -Milliseconds 500
|
||||
$doc.CloseDoc()
|
||||
$qv.Quit()
|
||||
Write-Host "DONE"
|
||||
Loading…
Reference in a new issue