【问题标题】:Can powershell combine data from multiple rows into one row and then "split" the data below that row for multiple "key" valuespowershell可以将多行中的数据组合成一行,然后将该行下方的数据“拆分”为多个“键”值
【发布时间】:2019-08-30 00:55:26
【问题描述】:

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

我已经完成了一半,我已将拆分数据全部映射到正确的列和行,但我不知道如何将数据合并到所有拆分数据之上的每个帐户的一行中。

以下是计费程序的数据来源:

BillingDate,Type,Acct,Customer,Code,Description,Qty,Price,Extension,GLCode
8/1/19,MRC,1003,Test Account,CONF6,CONFERENCE CALL RATE - $0.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 的新手。任何帮助将不胜感激!

【问题讨论】:

  • 你能发布一个输入样本吗?
  • @swestner 当然!对不起,我应该把它包含在帖子中。我现在已经编辑了帖子以包含它。
  • @mklement0 我很抱歉,那是我尝试的东西的一些剩余物。感谢您指出这一点。

标签: powershell csv quickbooks


【解决方案1】:

刚刚阅读了有关 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 始终包含我们不想要的标题行,因此我们将其去掉。

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 };

文件头

我们只是将这些作为文字字符串写到文件中:

# 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;

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

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

希望这会有所帮助..

M

【讨论】:

  • 这太不可思议了。我明天要看看这个,但它看起来和我想要的完全一样!你有任何捐赠页面或任何东西吗?很想送你一杯咖啡/啤酒来为你提供帮助。
  • @JonathanReynolds - 我很感激这个提议,但没必要,而且我也没有捐赠页面!对我来说,编写代码和解释是很好的做法,以便其他人能够理解它们:-)。
  • 工作的好伙伴!标记为答案,感谢您的所有帮助和慷慨 1000 倍。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多