powershell 能否将来自多行的数据合并为一行,然后 "split" 该行下方的数据用于多个 "key" 值

Can powershell combine data from multiple rows into one row and then "split" the data below that row for multiple "key" values

我正在尝试从没有 API 的计费系统中获取一些数据并以 Excel 格式向我们发送文件并将数据映射到新列和 headers 以匹配 Quickbooks 字段。

我已经完成一半了,我已将拆分数据全部映射到正确的列和行,但我无法弄清楚如何将所有拆分数据上方的每个帐户的数据合并为一行。

以下是数据来自计费程序的方式:

BillingDate,Type,Acct,Customer,Code,Description,Qty,Price,Extension,GLCode
8/1/19,MRC,1003,Test Account,CONF6,CONFERENCE CALL RATE - [=12=].06/CPM/LEG,1,0.00,0.00,4202
8/1/19,MRC,1003,Test Account,INTL,INTERNATIONAL RATE PLAN,1,0.00,0.00,4202
8/1/19,MRC,1003,Test Account,MASU,MASS ANNOUNCEMENT,1,0.00,0.00,4202
8/1/19,MRC,1003,Test Account,UNLD,UNLIMITED LONG DISTANCE,1,0.00,0.00,4202
8/1/19,MRC,1003,Test Account,W911,911 SERVICE,1,0.00,0.00,4202
8/1/19,MRC,1003,Test Account,WDA,DIRECTORY ASSISTANCE,1,0.00,0.00,4202
8/1/19,MRC,1003,Test Account,WDIDNN,DID NUMBERS - NATIONAL,42,0.00,0.00,4202
8/1/19,MRC,1003,Test Account,WIPTL1,SIP TRUNK,1,375.00,375.00,4202
8/1/19,MRC,1003,Test Account,WMGRT,MANAGED ROUTER,1,50.00,50.00,4202
8/1/19,Prorates,1003,Test Account,WIPTL1,SIP TRUNK,1,362.90,362.90,4202
8/1/19,Prorates,1003,Test Account,WMGRT,MANAGED ROUTER,1,48.39,48.39,4202
8/1/19,Tax Detail,1003,Test Account,00-59,FCC Cost Recovery Fee,1,1.43,1.43,3400
8/1/19,Tax Detail,1003,Test Account,00-60,Federal Telecommunications Relay Services Fund,1,13.44,13.44,3400
8/1/19,Tax Detail,1003,Test Account,00-R1,Federal Recovery Fee V001-002,1,103.31,103.31,3400
8/1/19,Tax Detail,1003,Test Account,01-01,State Sales Tax - Intrastate Telecom,1,30.47,30.47,3410
8/1/19,Tax Detail,1003,Test Account,02-02,County Sales Tax,1,5.41,5.41,3410
8/1/19,Tax Detail,1003,Test Account,02-33,911 Surcharge (Local),1,5.00,5.00,3400

下面是我目前的PS脚本:

$path = "pathtofile"
$DataFile = $path + "source.csv"
$ExportedFileCSV = $path + "final.csv"

$dataInput = Import-Csv $DataFile
$dataOutput = Import-Csv $ExportedFileCSV

$dataInput | ForEach-Object {

    $newData = $_
    $newRecordProperties = [ordered]@{
        "TRNS"="TRNS",$newData.null,"INVOICE",$newData.BillingDate
        "SPL"="SPL"
        "SPLID"=$newData.null
        "TRNSTYPE"="INVOICE"
        "DATE"=$newData.BillingDate
        "NAME"=$newData.Customer
        "DOCNUM"=$newData.null
        "DUEDATE"=$newdata.null
        "ACCNT"=$newData.GLCode
        "AMOUNT"=-$newData.Price
        "EXTRA"=-$newData.Extension
    }
    $newRecord = new-object psobject -Property $newRecordProperties
    Write-Output $newRecord
} | convertto-csv -NoTypeInformation -Delimiter "," | % {$_ -replace '"',''} | Out-File $ExportedFileCSV

我希望输出如下所示:

!TRNS,TRNSID,TRNSTYPE,DATE,NAME,DOCNUM,DUEDATE,ACCNT,AMOUNT,PAID
!SPL,SPLID,TRNSTYPE,DATE,NAME,DOCNUM,DUEDATE,ACCNT,AMOUNT,EXTRA
!ENDTRNS,,,,,,,,,
TRNS,,INVOICE,08/01/2019,Test Account,080119-1003,08/01/2019,1200,995.35,N
SPL,,INVOICE,,,,,4202,-375.00
SPL,,INVOICE,,,,,4202,-50.00
SPL,,INVOICE,,,,,4202,-362.90
SPL,,INVOICE,,,,,4202,-48.39
SPL,,INVOICE,,,,,3400,-1.43
SPL,,INVOICE,,,,,3400,-13.44
SPL,,INVOICE,,,,,3400,-103.31
SPL,,INVOICE,,,,,3410,-30.47
SPL,,INVOICE,,,,,3410,-5.41
SPL,,INVOICE,,,,,3400,-5.00
SPL,,INVOICE,,,,,,0.00,AUTOSTAX
ENDTRNS,,,,,,,,,

相反,这是我得到的:

SPL,SPLID,TRNSTYPE,DATE,NAME,DOCNUM,DUEDATE,ACCNT,AMOUNT,EXTRA
SPL,,INVOICE,8/1/19,Test Account,,,,-375,-375
SPL,,INVOICE,8/1/19,Test Account,,,,-50,-50
SPL,,INVOICE,8/1/19,Test Account,,,,-362.9,-362.9
SPL,,INVOICE,8/1/19,Test Account,,,,-48.39,-48.39
SPL,,INVOICE,8/1/19,Test Account,,,,-1.43,-1.43
SPL,,INVOICE,8/1/19,Test Account,,,,-13.44,-13.44
SPL,,INVOICE,8/1/19,Test Account,,,,-103.31,-103.31
SPL,,INVOICE,8/1/19,Test Account,,,,-30.47,-30.47
SPL,,INVOICE,8/1/19,Test Account,,,,-5.41,-5.41
SPL,,INVOICE,8/1/19,Test Account,,,,-5,-5

我已经为此工作了大约 2 周,当然,我是 Powershell 的新手。任何帮助将不胜感激!

根据刚才对 QuickBooks 的一些了解,您想要的输出文件似乎是一个 "Quickbooks Multi-Line Transaction Csv" 文件,格式如下:

!File Header 1
!File Header 2
!File Header 3
TRNS Record
SPL Record 1 
SPL Record 2
... etc ...
SPL Record N
SPL Tax Record
ENDTRNS Record

如果我们分解它,我们可以依次写出每个部分以生成所需的输出文件。不过,首先,我们需要一些辅助函数来生成 csv 行,因为默认的 ConvertTo-Csv cmdlet 总是包含 header 行,我们不需要它,所以我们将删除关了。

function ConvertTo-CsvRow
{
    param( $InputObject )
    # convert the input object into a csv record and remove the header line
    return ConvertTo-Csv -InputObject $InputObject -NoTypeInformation -Delimiter "," `
        | Select-Object -Skip 1 `
        | % { $_ -replace '"','' };
}

然后,我们将读取输入文件并对其进行一些处理:

$splits = Import-Csv $DataFile
foreach( $split in $splits )
{
    # convert some strings into the appropriate types
    $split.BillingDate = [datetime]::ParseExact($split.BillingDate, "d/M/yy", $null);
    $split.Price       = [decimal]::Parse($split.Price);
}
# remove lines with zero value
$splits = $splits | where-object { $_.Price -ne 0 };

文件Headers

我们将把它们作为文字字符串写到文件中:

# write file headers
Set-Content -Path $exportFile -Value "!TRNS,TRNSID,TRNSTYPE,DATE,NAME,DOCNUM,DUEDATE,ACCNT,AMOUNT,PAID";
Add-Content -Path $exportFile -Value "!SPL,SPLID,TRNSTYPE,DATE,NAME,DOCNUM,DUEDATE,ACCNT,AMOUNT,EXTRA";
Add-Content -Path $exportFile -Value "!ENDTRNS,,,,,,,,,";

TRNS 记录

此行包含一些摘要数字 - 我们假设其中一些可以从输入文件中的第一个拆分中获取,并且 AMOUNT 需要通过将 Price 来自所有拆分记录:

# write TRNS header
$split0 = $splits[0];
$trns = new-object PSObject -Property ([ordered] @{
    "TRNS"     = "TRNS"
    "TRNSID"   = $null
    "TRNSTYPE" = "INVOICE"
    "DATE"     = $split0.BillingDate.ToString("dd/MM/yyyy")
    "NAME"     = $split0.Customer
    "DOCNUM"   = $split0.BillingDate.ToString("ddMMyy") + "-" + $split0.Acct
    "DUEDATE"  = $split0.BillingDate.ToString("dd/MM/yyyy")
    "ACCNT"    = 1200
    "AMOUNT"   = ($splits | Measure-Object "Price" -Sum | Select-Object -Expand "Sum").ToString("F")
    "PAID"     = "N"
})
$trnsCsv = ConvertTo-CsvRow -InputObject $trns;
Add-Content -Path $exportFile -Value $trnsCsv;

SPL 记录

我们只需要像您的原始代码一样或多或少地遍历每个输入记录,但对日期格式和小数位应用一些输出格式:

# write splits
foreach( $split in $splits )
{
   $spl = new-object PSObject -Property ([ordered] @{
        "SPL"      = "SPL"
        "SPLID"    = $null
        "TRNSTYPE" = "INVOICE"
        "DATE"     = $null
        "NAME"     = $null
        "DOCNUM"   = $null
        "DUEDATE"  = $null
        "ACCNT"    = $split.GLCode
        "AMOUNT"   = (-$split.Price).ToString("F")
    });
    $splCsv = ConvertTo-CsvRow -InputObject $spl;
    Add-Content -Path $exportFile -Value $splCsv;
};

SPL 税务记录

我不知道我是否正确解释了这一点,所以它可能在所有情况下都不需要,并且可能必须根据输入文件中的拆分来计算,但下面的代码会产生你的所需的输出文件:

# write SPL tax record
$spl = new-object PSObject -Property ([ordered] @{
    "SPL"      = "SPL"
    "SPLID"    = $null
    "TRNSTYPE" = "INVOICE"
    "DATE"     = $null
    "NAME"     = $null
    "DOCNUM"   = $null
    "DUEDATE"  = $null
    "ACCNT"    = $null
    "AMOUNT"   = ([decimal] 0).ToString("F")
    "EXTRA"    = "AUTOSTAX"
})
$splCsv = ConvertTo-CsvRow -InputObject $spl;
Add-Content -Path $exportFile -Value $splCsv;

ENDTRNS 页脚

最后,ENDTRNS 页脚...

# write ENDTRNS footer
$endtrns = new-object PSObject -Property ([ordered] @{
    "ENDTRNS" = "ENDTRNS"
    "empty1"  = $null
    "empty2"  = $null
    "empty3"  = $null
    "empty4"  = $null
    "empty5"  = $null
    "empty6"  = $null
    "empty7"  = $null
    "empty8"  = $null
    "empty9"  = $null
})
$endtrnsCsv = ConvertTo-CsvRow -InputObject $endtrns;
Add-Content -Path $exportFile -Value $endtrnsCsv;

如果您 运行 一个接一个地使用所有这些代码块,您应该会发现它会生成准确的 byte-for-byte 所需输出文件。为每一行调用 ConvertTo-Csv 可能会降低性能,但我们基本上是在滥用它一次生成一行输出 csv 文件。

请注意,如果您希望输入中有多个 TRNS ... ENDTRNS,则以上内容可能需要修改 - 您需要以某种方式对拆分记录进行分组,并将每个组写入单独的 TRNS ... ENDTRNS 部分。

希望这对您有所帮助..