createSupportInvoices.ps1
<#
.SYNOPSIS Creates invoices based on timecockpit bookings on support projects .DESCRIPTION Workpackages are created as invoice position, which are closed, billable and not billed. The workpackage have no bookings later than the given date (to avoid having closed booking from the next month already in the invoice). .PARAMETER tcDBCredential Credential for Timecockpit DB. If not given they will be requested and stored, if used, in CredentialManager. .PARAMETER useCredentialsManager If credentialmanager should be used. Default is $True. .PARAMETER to If task has bookings later than this date, it will be omited. Defaults to last day of previous month .EXAMPLE Creates invoices up to january .\createSupportInvoices 2019-01-31 #> [CmdletBinding(SupportsShouldProcess=$true)] param( [PSCredential] $tcDBCredential, [Boolean] $useCredentialsManager = $True, [DateTime] $to ) # check prerequisites $tcModule = Get-Module -ListAvailable -Name "TimeCockpit"; if (!$tcModule) { throw "Module 'TimeCockpit' needed. Please install executing 'Install-Module -Name TimeCockpit' as Administrator."; } Write-Information "Using TimeCockpit Module v$($tcModule.Version)." if(!$to) { $now = Get-Date $to = Get-Date -Year $now.Year -Month $now.Month -Day 1 } else { $to = Get-Date -Year $to.Year -Month $to.Month -Day $to.Day } Write-Output "Will create Invoices for closed, billable and not billed timerecords having all booking before $($to.ToString('yyyy-MM-dd'))..."; $query = @" select Customer.APP_Code as 'CustomerCode', Project.APP_Code as 'ProjectCode', Task.APP_Code as 'TaskCode', Task.APP_TaskUuid as 'TaskUuid', min(Timesheet.APP_BeginTime) as 'Start', max(Timesheet.APP_EndTime) as 'End', Task.APP_Description as 'TaskDescription', Task.APP_Closed as 'TaskClosed', isnull( (select sum(cast(DATEDIFF(minute, Timesheet.APP_BeginTime, Timesheet.APP_EndTime) as float)) / 60 from edhrqnfxua.APP_Timesheet as Timesheet join edhrqnfxua.APP_Task as TaskInner on TaskInner.APP_TaskUuid = Timesheet.Relation_APP_Task where TaskInner.APP_TaskUuid = Task.APP_TaskUuid and TaskInner.APP_NoBilling = 0 and Timesheet.APP_NoBilling = 0 and (Timesheet.APP_HourlyRate is null or Timesheet.APP_HourlyRate > 0) ) , 0) as 'DurationBilled', isnull( (select sum(cast(DATEDIFF(minute, Timesheet.APP_BeginTime, Timesheet.APP_EndTime) as float)) / 60 from edhrqnfxua.APP_Timesheet as Timesheet join edhrqnfxua.APP_Task as TaskInner on TaskInner.APP_TaskUuid = Timesheet.Relation_APP_Task where TaskInner.APP_TaskUuid = Task.APP_TaskUuid and (TaskInner.APP_NoBilling = 1 or Timesheet.APP_NoBilling = 1 or Timesheet.APP_HourlyRate = 0) ) , 0) as 'DurationUnbilled', max(case when Task.APP_HourlyRate is null and Project.APP_HourlyRate is null then Customer.APP_HourlyRate when Task.APP_HourlyRate is null then Project.APP_HourlyRate else Task.APP_HourlyRate end) as 'HourlyRate' from edhrqnfxua.APP_Timesheet as Timesheet join edhrqnfxua.APP_Project as Project on Project.APP_ProjectUuid = Timesheet.Relation_APP_Project join edhrqnfxua.APP_Customer as Customer on Customer.APP_CustomerUuid = Project.Relation_APP_Customer join edhrqnfxua.APP_Task as Task on Task.APP_TaskUuid = Timesheet.Relation_APP_Task where Project.APP_Code= 'Support' and Customer.APP_Code not in ('GuidNew') and Project.APP_Billable = 1 and Task.APP_NoBilling = 0 and Timesheet.APP_NoBilling = 0 and Timesheet.APP_HourlyRateBilled is null group by Customer.APP_Code, Project.APP_Code, Task.APP_Code, Task.APP_TaskUuid, Task.APP_Description, Task.APP_Closed having max(Timesheet.APP_EndTime) < '$($to.ToString('yyyy-MM-dd'))' order by Customer.APP_Code, Task.APP_Closed desc, max(Timesheet.APP_EndTime) "@ Connect-TCDB -Credential $tcDBCredential -UseCredentialsManager $useCredentialsManager; Write-Information "Fetching Timerecords from DB..."; $items = (Invoke-TCDBSqlCmd -Query $query) Write-Information "Fetched $($items.Count) records."; $customerCode = $Null; foreach($item in $items) { if($customerCode -ne $item.CustomerCode) { $customerCode = $item.CustomerCode; Write-Output "Details zu $($item.CustomerCode)"; } $lineAmount = [math]::Round($item.DurationBilled * $item.HourlyRate, 2); $duration = $Null; if($item.Start.Date -eq $item.End.Date) { $duration = "am $($item.Start.ToString('dd.MM.yyyy'))"; } else { $duration = "im Zeitraum $($item.Start.ToString('dd.MM.yyyy'))-$($item.End.ToString('dd.MM.yyyy'))"; } if($item.TaskClosed) { if($item.DurationBilled -gt 0) { $unbilledInfo = "" if($item.DurationUnbilled -gt 0) { $unbilledInfo = "(+$($item.DurationUnbilled)h nicht verrechnet) " } Write-Output " geschlossen : $($item.TaskCode) ($($item.TaskDescription)) - $duration $($item.DurationBilled)h ${unbilledInfo}zu Euro $($item.HourlyRate)/h = Euro $lineAmount"; } else { Write-Output " geschlossen : $($item.TaskCode) ($($item.TaskDescription)) - $duration $($item.DurationUnbilled)h (nicht verrechnet) = Euro 0.00"; } } else { Write-Output " noch offen : $($item.TaskCode) ($($item.TaskDescription)) - $duration $($item.DurationBilled)h zu Euro $($item.HourlyRate)/h = Euro $lineAmount"; } } |