Added -Calculate, fixed formula creation to be EPPlus-friendly

This commit is contained in:
jhoneill
2018-08-13 20:58:10 +01:00
parent 121346f939
commit 2793ff1c21
9 changed files with 85 additions and 57 deletions

View File

@@ -22,6 +22,8 @@
Some objects duplicate existing properties by adding aliases, or have Script properties which take a long time to return a value and slow the export down, if specified this removes these properties
.PARAMETER ExcludeProperty
Specifies properties which may exist in the target data but should not be placed on the worksheet.
.PARAMETER Calculate
If specified a recalculation of the worksheet will be requested before saving.
.PARAMETER Title
Text of a title to be placed in the top left cell.
.PARAMETER TitleBold
@@ -372,6 +374,7 @@
[OfficeOpenXml.ExcelPackage]$ExcelPackage,
[Parameter(ValueFromPipeline = $true)]
$TargetData,
[Switch]$Calculate,
[Switch]$Show,
[String]$WorksheetName = 'Sheet1',
[String]$Password,
@@ -497,8 +500,8 @@
break
}
{($_ -is [String]) -and ($_[0] -eq '=')} {
#region Save an Excel formula
$TargetCell.Formula = $_
#region Save an Excel formula - we need = to spot the formula but the EPPLUS won't like it if we include it (Excel doesn't care if is there or not)
$TargetCell.Formula = ($_ -replace '^=','')
if ($setNumformat) {$targetCell.Style.Numberformat.Format = $Numberformat }
#Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$_' as formula"
break
@@ -560,15 +563,20 @@
$Path = $pkg.File
}
Else { $pkg = Open-ExcelPackage -Path $Path -Create -KillExcel:$KillExcel -Password:$Password}
}
Catch {throw "Could not open Excel Package $path"}
if ($NoClobber) {Write-Warning -Message "-NoClobber parameter is no longer used" }
Try {
$params = @{WorksheetName=$WorksheetName}
if ($NoClobber) {Write-Warning -Message "-NoClobber parameter is no longer used" }
foreach ($p in @("ClearSheet", "MoveToStart", "MoveToEnd", "MoveBefore", "MoveAfter", "Activate")) {if ($PSBoundParameters[$p]) {$params[$p] = $PSBoundParameters[$p]}}
$ws = $pkg | Add-WorkSheet @params
if ($ws.Name -ne $WorksheetName) {
Write-Warning -Message "The Worksheet name has been changed from $WorksheetName to $($ws.Name), this may cause errors later."
$WorksheetName = $ws.Name
}
}
Catch {throw "Could not get worksheet $worksheetname"}
try {
foreach ($format in $ConditionalFormat ) {
switch ($format.formatter) {
"ThreeIconSet" {Add-ConditionalFormatting -WorkSheet $ws -ThreeIconsSet $format.IconType -range $format.range -reverse:$format.reverse }
@@ -576,7 +584,9 @@
"FiveIconSet" {Add-ConditionalFormatting -WorkSheet $ws -FiveIconsSet $format.IconType -range $format.range -reverse:$format.reverse }
}
}
}
catch {throw "Error applying confitional formatting to worksheet"}
try {
if ($Append -and $ws.Dimension) {
#if there is a title or anything else above the header row, append needs to be combined wih a suitable startrow parameter
$headerRange = $ws.Dimension.Address -replace "\d+$", $StartRow
@@ -965,6 +975,11 @@
catch {Write-Warning -Message "Failed processing CellStyleSB in worksheet '$WorksheetName': $_"}
}
if ($Calculate) {
try { [OfficeOpenXml.CalculationExtension]::Calculate($ws) }
Catch { Write-Warning "One or more errors occured while calculating, save will continue, but there may be errors in the workbook."}
}
if ($Password) {
try {
$ws.Protection.SetPassword($Password)

View File

@@ -64,10 +64,16 @@ Function Close-ExcelPackage {
#Save file with a new name (ignored if -NoSave Specified).
$SaveAs,
[ValidateNotNullOrEmpty()]
[String]$Password
[String]$Password,
#Attempt to recalculation the workbook before saving
[switch]$Calculate
)
if ( $NoSave) {$ExcelPackage.Dispose()}
else {
if ($Calculate) {
try { [OfficeOpenXml.CalculationExtension]::Calculate($ExcelPackage.Workbook) }
Catch { Write-Warning "One or more errors occured while calculating, save will continue, but there may be errors in the workbook."}
}
if ($SaveAs) {
$SaveAs = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($SaveAs)
if ($Password) {$ExcelPackage.SaveAs( $SaveAs, $Password ) }

View File

@@ -51,7 +51,8 @@ Install-Module ImportExcel -scope CurrentUser
```PowerShell
Install-Module ImportExcel
```
# New to Aug 12th
# New to Aug 13th
- Added -Calculate switch to Export-Excel and Close-Excel Package; EPPlus needs formulas to OMIT the leading = sign so where formula is set it now strips a leading =
- Added -PivotTotals parameter where there was already -NoTotalsInPivot new one allows None, Both, Rows, Columns. (#415)
- When appending Export-Excel only extended tables and ranges if they were explicitly specified. It now does it automatically.
- Compare and Merge worksheet originally had a problem with > 26 columns, I fixed merge turns out I hadn't fixed compare ... I have now

View File

@@ -79,7 +79,7 @@
[float]$Width,
#Set the inserted data to be a named range (ignored if header is not specified)
[Switch]$AutoNameRange,
#If Sepecified returns the range of cells which affected
#If Sepecified returns the range of cells which were affected
[switch]$ReturnRange,
#If Specified, return an ExcelPackage object to allow further work to be done on the file.
[switch]$PassThru
@@ -90,7 +90,7 @@
#In a script block to build a formula, we may want any of corners or the columnname,
#if column and startrow aren't specified, assume first unused column, and first row
if (-not $StartRow) {$startRow = $Worksheet.Dimension.Start.Row }
$StartColumn = $Worksheet.Dimension.Start.Column
$startColumn = $Worksheet.Dimension.Start.Column
$endColumn = $Worksheet.Dimension.End.Column
$endRow = $Worksheet.Dimension.End.Row
if ($Column -lt 2 ) {$Column = $endColumn + 1 }
@@ -110,7 +110,7 @@
Write-Verbose -Message $cellData
}
else { $cellData = $Value}
if ($cellData -match "^=") { $Worksheet.Cells[$Row, $Column].Formula = $cellData }
if ($cellData -match "^=") { $Worksheet.Cells[$Row, $Column].Formula = ($cellData -replace '^=','') } #EPPlus likes formulas with no = sign; Excel doesn't care
elseif ( [System.Uri]::IsWellFormedUriString($cellData , [System.UriKind]::Absolute)) {
# Save a hyperlink : internal links can be in the form xl://sheet!E419 (use A1 as goto sheet), or xl://RangeName
if ($cellData -match "^xl://internal/") {
@@ -123,8 +123,8 @@
$Worksheet.Cells[$Row, $Column].Style.Font.Color.SetColor([System.Drawing.Color]::Blue)
$Worksheet.Cells[$Row, $Column].Style.Font.UnderLine = $true
}
else { $Worksheet.Cells[$Row, $Column].Value = $cellData }
if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $Column].Style.Numberformat.Format = 'm/d/yy h:mm' } # This is not a custom format, but a preset recognized as date and localized.
else { $Worksheet.Cells[$Row, $Column].Value = $cellData }
if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $Column].Style.Numberformat.Format = 'm/d/yy h:mm' } # This is not a custom format, but a preset recognized as date and localized.
}}
#region Apply formatting
$params = @{}

View File

@@ -77,9 +77,9 @@
[int]$TextRotation ,
#Set cells to a fixed hieght
[float]$Height,
#If Sepecified returns the range of cells which affected
#If Sepecified returns the range of cells which were affected
[switch]$ReturnRange,
#If Specified, return a row object to allow further work to be done
#If Specified, return a row object to allow further work to be done
[switch]$PassThru
)
@@ -102,14 +102,14 @@
#Fill in the data
if ($PSBoundParameters.ContainsKey('Value')) {foreach ($column in ($StartColumn..$EndColumn)) {
#We might want the column name in a script block
$ColumnName = [OfficeOpenXml.ExcelCellAddress]::new(1,$column).Address -replace "1",""
$columnName = [OfficeOpenXml.ExcelCellAddress]::new(1,$column).Address -replace "1",""
if ($Value -is [scriptblock] ) {
#re-create the script block otherwise variables from this function are out of scope.
$cellData = & ([scriptblock]::create( $Value ))
Write-Verbose -Message $cellData
}
else{$cellData = $Value}
if ($cellData -match "^=") { $Worksheet.Cells[$Row, $column].Formula = $cellData }
if ($cellData -match "^=") { $Worksheet.Cells[$Row, $column].Formula = ($cellData -replace '^=','') } #EPPlus likes formulas with no = sign; Excel doesn't care
elseif ( [System.Uri]::IsWellFormedUriString($cellData , [System.UriKind]::Absolute)) {
# Save a hyperlink : internal links can be in the form xl://sheet!E419 (use A1 as goto sheet), or xl://RangeName
if ($cellData -match "^xl://internal/") {
@@ -122,8 +122,8 @@
$Worksheet.Cells[$Row, $Column].Style.Font.Color.SetColor([System.Drawing.Color]::Blue)
$Worksheet.Cells[$Row, $Column].Style.Font.UnderLine = $true
}
else { $Worksheet.Cells[$Row, $column].Value = $cellData }
if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $column].Style.Numberformat.Format = 'm/d/yy h:mm' } # This is not a custom format, but a preset recognized as date and localized.
else { $Worksheet.Cells[$Row, $column].Value = $cellData }
if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $column].Style.Numberformat.Format = 'm/d/yy h:mm' } #This is not a custom format, but a preset recognized as date and localized.
}}
#region Apply formatting
$params = @{}
@@ -132,7 +132,7 @@
'BorderAround', 'BackgroundColor', 'BackgroundPattern', 'PatternColor')) {
if ($PSBoundParameters.ContainsKey($p)) {$params[$p] = $PSBoundParameters[$p]}
}
$theRange = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$Row]C[$StartColumn]:R[$Row]C[$EndColumn]",0,0)
$theRange = [OfficeOpenXml.ExcelAddress]::New($Row, $StartColumn, $Row, $EndColumn)
if ($params.Count) {
Set-Format -WorkSheet $Worksheet -Range $theRange @params
}

View File

@@ -133,7 +133,7 @@
$Address.Style.VerticalAlignment = $VerticalAlignment
}
if ($PSBoundParameters.ContainsKey('Value')) {
if ($Value -like '=*') {$Address.Formula = $Value}
if ($Value -like '=*') {$Address.Formula = ($Value -replace'^=','')} #EPPlus likes formulas with no = sign; Excel doesn't care
else {
$Address.Value = $Value
if ($Value -is [DateTime]) {
@@ -143,7 +143,7 @@
}
if ($PSBoundParameters.ContainsKey('Formula')) {
$Address.Formula = $Formula
$Address.Formula = ( $Formula -replace '^=','')
}
if ($PSBoundParameters.ContainsKey('NumberFormat')) {
$Address.Style.Numberformat.Format = (Expand-NumberFormat $NumberFormat)
@@ -206,7 +206,6 @@
$Address -is [OfficeOpenXml.ExcelColumn] ) {$Address.Hidden = [boolean]$Hidden}
else {Write-Warning -Message ("Can hide a row or a column but not a {0} object" -f ($Address.GetType().name)) }
}
}
}
}

View File

@@ -4,8 +4,8 @@ Remove-item -Path $path1, $path2 -ErrorAction SilentlyContinue
$ProcRange = Get-Process | Export-Excel $path1 -DisplayPropertySet -WorkSheetname Processes -ReturnRange
if ((Get-Culture).NumberFormat.CurrencySymbol -eq "£") {$OtherCurrencySymbol = "$"}
else {$OtherCurrencySymbol = "£"}
if ((Get-Culture).NumberFormat.CurrencySymbol -eq "<EFBFBD>") {$OtherCurrencySymbol = "$"}
else {$OtherCurrencySymbol = "<EFBFBD>"}
[PSCustOmobject][Ordered]@{
Date = Get-Date
Formula1 = '=SUM(F2:G2)'
@@ -25,8 +25,8 @@ else {$OtherCurrencySymbol = "
StrE164Phone = '+32 (444) 444 4444'
StrAltPhone1 = '+32 4 4444 444'
StrAltPhone2 = '+3244444444'
StrLeadSpace = ' 123'
StrTrailSpace = '123 '
StrLeadSpace = ' 123'
StrTrailSpace = '123 '
Link1 = [uri]"https://github.com/dfinke/ImportExcel"
Link2 = "https://github.com/dfinke/ImportExcel" # Links are not copied correctly, hopefully this will be fixed at some future date
} | Export-Excel -NoNumberConversion IPAddress, StrLeadZero, StrAltPhone2 -WorkSheetname MixedTypes -Path $path2
@@ -50,14 +50,16 @@ Describe "Copy-Worksheet" {
$excel = Open-ExcelPackage -Path $path2
$ws = $Excel.Workbook.Worksheets[3]
}
it "Inserted a worksheet with the expected name, number of rows and number of columns " {
it "Copied a worksheet, giving the expected name, number of rows and number of columns " {
$Excel.Workbook.Worksheets.count | Should be 3
$ws | Should not benullorEmpty
$ws.Name | Should be "CopyOfMixedTypes"
$ws.Dimension.Columns | Should be 22
$ws.Dimension.Rows | Should be 2
}
it "Copied the expected data into the worksheet " {
$ws.Cells[2, 1].Value.Gettype().name | Should be 'DateTime'
$ws.Cells[2, 2].Formula | Should be '=SUM(F2:G2)'
$ws.Cells[2, 2].Formula | Should be 'SUM(F2:G2)'
$ws.Cells[2, 5].Value.GetType().name | Should be 'String'
$ws.Cells[2, 6].Value.GetType().name | Should be 'String'
$ws.Cells[2, 18].Value.GetType().name | Should be 'String'

View File

@@ -10,7 +10,7 @@ Describe ExportExcel {
Context "#Example 1 # Creates and opens a file with the right number of rows and columns" {
$path = "$env:TEMP\Test.xlsx"
Remove-item -Path $path -ErrorAction SilentlyContinue
$processes = Get-Process
$processes = Get-Process | select-object -first 100
$propertyNames = $Processes[0].psobject.properties.name
$rowcount = $Processes.Count
$Processes | Export-Excel $path #-show
@@ -66,7 +66,7 @@ Describe ExportExcel {
Context " # NoAliasOrScriptPropeties -ExcludeProperty and -DisplayPropertySet work" {
$path = "$env:TEMP\Test.xlsx"
Remove-item -Path $path -ErrorAction SilentlyContinue
$processes = Get-Process
$processes = Get-Process | Select-Object -First 100
$propertyNames = $Processes[0].psobject.properties.where( {$_.MemberType -eq 'Property'}).name
$rowcount = $Processes.Count
#TestCreating a range with a name which needs illegal chars removing
@@ -151,7 +151,7 @@ Describe ExportExcel {
Remove-item -Path $path -ErrorAction SilentlyContinue
[PSCustOmobject][Ordered]@{
Date = Get-Date
Formula1 = '=SUM(F2:G2)'
Formula1 = '=SUM(S2:T2)'
String1 = 'My String'
Float = [math]::pi
IPAddress = '10.10.25.5'
@@ -174,7 +174,7 @@ Describe ExportExcel {
Link2 = "https://github.com/dfinke/ImportExcel"
Link3 = "xl://internal/sheet1!A1"
Link4 = "xl://internal/sheet1!C5"
} | Export-Excel -NoNumberConversion IPAddress, StrLeadZero, StrAltPhone2 -Path $path
} | Export-Excel -NoNumberConversion IPAddress, StrLeadZero, StrAltPhone2 -Path $path -Calculate
it "Created a new file " {
Test-Path -Path $path -ErrorAction SilentlyContinue | Should be $true
}
@@ -192,12 +192,15 @@ Describe ExportExcel {
$ws.Cells[2, 1].Value.Gettype().name | Should be 'DateTime'
}
it "Set a formula in Cell B2 " {
$ws.Cells[2, 2].Formula | Should be '=SUM(F2:G2)'
$ws.Cells[2, 2].Formula | Should be 'SUM(S2:T2)'
}
it "Forced a successful calculation of the Value in Cell B2 " {
$ws.Cells[2, 2].Value | Should be 246
}
it "Set strings in Cells E2, F2 and R2 (no number conversion) " {
$ws.Cells[2, 5].Value.GetType().name | Should be 'String'
$ws.Cells[2, 6].Value.GetType().name | Should be 'String'
$ws.Cells[2, 18].Value.GetType().name | Should be 'String'
$ws.Cells[2, 5].Value.GetType().name | Should be 'String'
$ws.Cells[2, 6].Value.GetType().name | Should be 'String'
$ws.Cells[2, 18].Value.GetType().name | Should be 'String'
}
it "Set numbers in Cells K2,L2,M2 (diferent Negative integer formats) " {
($ws.Cells[2, 11].Value -is [valuetype] ) | Should be $true
@@ -280,11 +283,11 @@ Describe ExportExcel {
}
it "Set a date in Cell A1 " {
$ws.Cells[1, 1].Value.Gettype().name | Should be 'DateTime'
$ws.Cells[1, 1].Value.Gettype().name | Should be 'DateTime'
}
it "Set a formula in Cell B1 " {
$ws.Cells[1, 2].Formula | Should be '=SUM(F1:G1)'
$ws.Cells[1, 2].Formula | Should be 'SUM(F1:G1)'
}
it "Set strings in Cells E1 and F1 " {

View File

@@ -137,7 +137,7 @@ Describe "Set-Column, Set-Row and Set Format" {
$ws = $excel.Workbook.Worksheets["Sheet1"]
$c = Set-Column -PassThru -Worksheet $ws -Heading "Total" -Value "=Quantity*Price" -NumberFormat "£#,###.00" -FontColor Blue -Bold -HorizontalAlignment Right -VerticalAlignment Top
$r = Set-Row -PassThru -Worksheet $ws -StartColumn 3 -BorderAround Thin -Italic -Underline -FontSize 14 -Value {"=sum($columnName`2:$columnName$endrow)" } -VerticalAlignment Bottom
$r = Set-Row -PassThru -Worksheet $ws -StartColumn 3 -BorderAround Thin -Italic -Underline -FontSize 14 -Value {"=sum($columnName`2:$columnName$endrow)" } -VerticalAlignment Bottom
Set-Format -Address $excel.Workbook.Worksheets["Sheet1"].cells["b3"] -HorizontalAlignment Right -VerticalAlignment Center -BorderAround Thick -BorderColor Red -StrikeThru
Set-Format -Address $excel.Workbook.Worksheets["Sheet1"].cells["c3"] -BorderColor Red -BorderTop DashDot -BorderLeft DashDotDot -BorderBottom Dashed -BorderRight Dotted
Set-Format -WorkSheet $ws -Range "E3" -Bold:$false -FontShift Superscript -HorizontalAlignment Left
@@ -153,7 +153,7 @@ Describe "Set-Column, Set-Row and Set Format" {
Set-Format -WorkSheet $ws -Range "D$rr" -Formula "=E$rr/C$rr" -Hidden -WarningVariable "BadHideWarnvar" -WarningAction SilentlyContinue
$rr ++
Set-Format -WorkSheet $ws -Range "B$rr" -Value ([datetime]::Now)
Close-ExcelPackage $excel
Close-ExcelPackage $excel -Calculate
$excel = Open-ExcelPackage $path
@@ -167,7 +167,8 @@ Describe "Set-Column, Set-Row and Set Format" {
$ws.Row(5).height | Should be 0
}
it "Set a column formula, with numberformat, color, bold face and alignment " {
$ws.cells["e2"].Formula | Should be "=Quantity*Price"
$ws.cells["e2"].Formula | Should be "Quantity*Price"
$ws.cells["e2"].Value | Should be 147.63
$ws.cells["e2"].Style.Font.Color.rgb | Should be "FF0000FF"
$ws.cells["e2"].Style.Font.Bold | Should be $true
$ws.cells["e2"].Style.Font.VerticalAlign | Should be "None"
@@ -189,7 +190,8 @@ Describe "Set-Column, Set-Row and Set Format" {
$ws.cells["E7"].style.Border.Left.Style | Should be "None"
$ws.cells["E7"].style.Border.Right.Style | Should be "Thin"
$ws.cells["C7"].style.Font.size | Should be 14
$ws.cells["C7"].Formula | Should be "=sum(C2:C6)"
$ws.cells["C7"].Formula | Should be "sum(C2:C6)"
$ws.cells["C7"].value | Should be 81
$ws.cells["C7"].style.Font.UnderLine | Should be $true
$ws.cells["C6"].style.Font.UnderLine | Should be $false
}
@@ -214,7 +216,7 @@ Describe "Set-Column, Set-Row and Set Format" {
Context "Set-Format value setting " {
it "Inserted a formula " {
$ws.Cells["D7"].Formula | Should be "=E7/C7"
$ws.Cells["D7"].Formula | Should be "E7/C7"
}
it "Inserted a value " {
$ws.Cells["B7"].Value | Should be "Total"
@@ -243,7 +245,7 @@ Describe "Set-Column, Set-Row and Set Format" {
Set-Column -Worksheet $ws -Heading "Age" -Value "=INT((NOW()-DateOfBirth)/365)"
Set-Format -Address $c,$ws.column(3) -NumberFormat 'Short Date' -AutoSize
Close-ExcelPackage -ExcelPackage $excel
Close-ExcelPackage -ExcelPackage $excel -Calculate
$excel = Open-ExcelPackage $path
$ws = $excel.Workbook.Worksheets["Sheet1"]
}