lazy100.psm1
|
$BoxUnassignedId = -1 $IgnoreBoxId = 0 $Script:TagBoxHT = @{} $Script:BoxToIdHT = @{} $Script:IdToBoxHT = @{} function Set-DotEnv { param( $EnvFile = (Join-Path $env:USERPROFILE '.lazy100') ) if (-not (Test-Path $EnvFile)) { Copy-Item (Join-Path $MyInvocation.MyCommand.Module.ModuleBase 'lazy100-sample.conf') $EnvFile return } Get-Content $EnvFile ` | Where-Object { $_ -match '=' -and $_ -notmatch '^#' } ` | ForEach-Object { $name, $value = $_ -split '=', 2; [Environment]::SetEnvironmentVariable($name.Trim(), $value.Trim().Trim('"').Trim("'"), 'Process') } } Set-DotEnv function Test-Configuration { param( $Variables = @("sage_host", "sage_db", "sage_tenant", "sage_authorization", "sage_employee", "manictime_exports", "lazy100_configs") ) $Variables | ForEach-Object { if ([string]::isNullOrEmpty([Environment]::GetEnvironmentVariable($_))) { Write-Warning "Environment Variable '$_' is not configured" } } } Test-Configuration function Import-TagBoxMapping { param( $MappingFile = (Join-Path $env:lazy100_configs 'tags.conf' -ErrorAction SilentlyContinue) ) if (-not (Test-Path $MappingFile)) { return } Get-Content -Path $MappingFile ` | Where-Object { $_ -match '=' -and $_ -notmatch '^#' } ` | ForEach-Object { $name, $value = $_ -split '=', 2; $Script:TagBoxHT[$name.Trim()] = $value.Trim().Trim('"').Trim("'") } } Import-TagBoxMapping function Get-TagBoxMapping { param( $Tag ) ($null -eq $Script:TagBoxHT[$Tag]) ? -1 : ($Script:TagBoxHT[$Tag] -eq "" ? 0 : $Script:TagBoxHT[$Tag]) } function Add-TagBoxMapping { param( $Tag, $Box ) $Script:TagBoxHT[$Tag] = $Box Export-TagBoxMapping } function Remove-TagBoxMapping { param( $Tag ) $Script:TagBoxHT.Remove($Tag) } function Export-TagBoxMapping { param( $MappingFile = (Join-Path $env:lazy100_configs 'tags.conf') ) $HT = $Script:TagBoxHT Set-Content -Path $MappingFile -Value (@($HT.Keys | Sort-Object | ForEach-Object { "$($_)=$($HT[$_])" }) -join "`n") } function Rename-DuplicateCsvHeaders { [CmdletBinding()] param( [Parameter(ValueFromPipeline)] [string]$Content, [string]$Delimiter = ',' ) process { $lines = $Content -split '\r?\n' if ($lines.Count -eq 0 -or $lines[0] -eq '') { $Content return } $seen = @{} $headers = foreach ($header in ($lines[0] -split [regex]::Escape($Delimiter))) { $name = $header.Trim('"') if ($seen.ContainsKey($name)) { $seen[$name]++ "${name}_$($seen[$name])" } else { $seen[$name] = 1 $name } } $lines[0] = $headers -join $Delimiter $lines -join "`n" } } function Invoke-FileChooser { param( $Path = $PWD.Path, $Filter, $Title, $OutputMode = 'Single' ) Get-ChildItem -Path $Path -Filter $Filter ` | Sort-Object Name -Descending ` | Out-ConsoleGridView -Title $Title -OutputMode Single } function Invoke-ManicTimeCsvProcessing { param( $File ) $csv = Get-Content -Path $File -Raw | Rename-DuplicateCsvHeaders | ConvertFrom-Csv $csv ` | Select-Object -SkipLast 1 ` | Select-Object ` @{n = 'tag'; e = { $_.'Tag 1' } }, @{n = 'box'; e = { Get-TagBoxMapping $_.'Tag 1' } }, @{n = 'name'; e = { Get-SageActivityBox -Id (Get-TagBoxMapping $_.'Tag 1') } }, @{n = 'billable'; e = { $_.Billable -ne 'Not billable' } }, @{n = 'date'; e = { ($_ | Get-Member -MemberType NoteProperty | Where-Object { $_.Name -match '\d{4}' }).Name } }, @{n = 'total'; e = { $_.Total } }, @{n = 'description'; e = { $_.Notes.Trim("'") } }, @{n = 'reference'; e = { ($_.Notes -match '\[([\w\-]+)\]' | ForEach-Object { $Matches[1] }) ?? "" } } } function Invoke-SageRestRequest { param( $Uri, $Method, $Headers, $Body ) if (-not $Headers) { $Headers = @{} $Headers.Add("Content-Type", "application/json") $Headers.Add("X-ApplicationId", "Abf") $headers.Add("X-MachineName", "lazy100") $Headers.Add("Accept", "application/json,application/atom+xml") $Headers.Add("User-Agent", 'lazy100') $Headers.Add("Authorization", $env:sage_authorization) } $response = Invoke-WebRequest -Uri $Uri -Method $Method -Headers $Headers -UseBasicParsing -ContentType 'application/json' -Body $Body -ErrorAction Stop return $response.Content ?? $response } function Invoke-SageActivityBoxesRequest { Invoke-SageRestRequest -Uri "https://$($env:sage_host)/sdata/ol/ControlCenterData/$($env:sage_db)%3B$($env:sage_tenant)/%24service/dtsADBoxSuche.106214717.LAGActivityDesk?include=%24children&language=de" -Method POST -Body '{ "PartKey": "lstADBoxSuche.106214717.LAGActivityDesk", "ClientCultureName": "de", "Configuration": { "SortFieldName": "BoxMatchcode", "Filters": [ { "FieldName": "Aktiv", "FilterType": "Equal", "FilterValue1": "-1", "IsFilterActive": true } ], "PartFields": [ { "Name": "BoxID" }, { "Name": "BoxMatchcode" } ] } }' } function Save-SageActivityBoxes { param( $BoxSaveFile = (Join-Path $env:lazy100_configs 'boxes.conf') ) try { $json = Invoke-SageActivityBoxesRequest | ConvertFrom-Json } catch { Write-Warning "Failed to get Activity boxes: $($_)" return } Set-Content -Path $BoxSaveFile -Value ( @($json.Rows | ForEach-Object { "$($_.BoxId)=$($_.BoxMatchCode)" }) -join "`n" ) } function Get-SageActivityBoxes { param( $BoxSaveFile = (Join-Path $env:lazy100_configs 'boxes.conf' -ErrorAction SilentlyContinue) ) if (-not (Test-Path $BoxSaveFile)) { return } Get-Content $BoxSaveFile ` | Where-Object { $_ -match '=' -and $_ -notmatch '^#' } ` | ForEach-Object { $name, $value = $_ -split '=', 2 [PSCustomObject]@{ Id = $name.Trim() Name = $value.Trim().Trim('"').Trim("'") } } } function Invoke-CacheSageActivityBoxes { Get-SageActivityBoxes | ForEach-Object { $Script:BoxToIdHT[$_.Name] = $_.Id $Script:IdToBoxHT[$_.Id] = $_.Name } } Invoke-CacheSageActivityBoxes function Get-SageActivityBox { param( $Id, $Name ) $Id ? $script:IdToBoxHT[$Id] : $script:BoxToIdHT[$Name] } function New-SageActivityEntry { param( [int]$Box, [DateTime]$Date, [float]$Total, [bool]$Billable, [string]$Description, [string]$Reference, [string]$Employee = $env:sage_employee, [bool]$TestMode = ($env:lazy100_testmode -eq 1) ) # TODO: build json from Hashtable $RequestHT = @{ BoxID = $Box Datum = $Date.ToString("yyyy-MM-dd") Mitarbeiter = $Employee Stunden = $Total Verrechenbar = $Billable ? -1 : 0 Referenz = $Reference Beschreibung = @($Description -split "`n") -join "\n" } $RequestMethod = 'POST' $RequestUri = "https://$($env:sage_host)/sdata/ol/MasterData/$($env:sage_db);$($env:sage_tenant)/strADBoxBuchung.106214717.LAGActivityDesk?language=de" if ($TestMode) { return @{ Method = $RequestMethod Uri = $RequestUri Body = $RequestHT } } $headers = @{} $headers.Add("Accept", "application/json,application/atom+xml") $headers.Add("X-ApplicationId", "Abf") $headers.Add("X-ContextToken", "Application") $headers.Add("X-PartContext", "ediADBoxBuchung.106214717.LAGActivityDesk") $headers.Add("X-PartContextType", "DataEdit") $headers.Add("X-MachineName", "lazy100") $headers.Add("User-Agent", "JIRACustomApp") $headers.Add("Authorization", $env:sage_authorization) $headers.Add("Content-Type", "application/json") $RequestBody = $RequestHT | ConvertTo-Json -Compress Invoke-SageRestRequest -Uri $RequestUri -Method $RequestMethod -Headers $headers -Body $RequestBody } function Invoke-Lazy100 { Set-DotEnv $File = Invoke-FileChooser -Title 'select ManicTime export' -Filter '*.csv' -Path $env:manictime_exports if (-not $File) { return } Invoke-ManicTimeCsvProcessing -File $File ` | Where-Object { $_.box -eq $BoxUnassignedId } ` | Select-Object -Expand tag -Unique ` | ForEach-Object { $tag = $_ Write-Host "Tag $tag is not assigned to a box, assigning..." Get-SageActivityBoxes ` | Out-ConsoleGridView -Title "Choose box for tag '$tag'" -OutputMode Single ` | ForEach-Object { Add-TagBoxMapping -Tag $tag -Box $_.Id } } Invoke-ManicTimeCsvProcessing -File $File ` | Where-Object { $_.box -notin @($BoxUnassignedId, $IgnoreBoxId) } ` | Out-ConsoleGridView -Title "Select entries to create in Sage Activity" ` | ForEach-Object { New-SageActivityEntry -Box $_.box -Date ([DateTime]::Parse($_.date)) -Total $_.total -Description $_.description -Reference $_.reference -Billable $_.billable } } if (-not (Get-Alias lazy100 -ErrorAction SilentlyContinue)) { New-Alias -Name lazy100 -Value "Invoke-Lazy100" } |