Add Sage Spain export artifacts
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
**********************
|
||||
nStart der Windows PowerShell-Aufzeichnung
|
||||
Startzeit: 20260427082528
|
||||
Benutzername: TRAFAGCH\koi
|
||||
RunAs-Benutzer: TRAFAGCH\koi
|
||||
Konfigurationsname:
|
||||
Computer: NB61258 (Microsoft Windows NT 10.0.26200.0)
|
||||
Hostanwendung: C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\Users\koi\source\repos\Ai\TrafagSalesExporter\.tmp_sap_probe\RunSapProbeInteractive.ps1 abap-activate Z_TEST3 --dry-run
|
||||
Prozess-ID: 452
|
||||
PSVersion: 5.1.26100.8115
|
||||
PSEdition: Desktop
|
||||
PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1.26100.8115
|
||||
BuildVersion: 10.0.26100.8115
|
||||
CLRVersion: 4.0.30319.42000
|
||||
WSManStackVersion: 3.0
|
||||
PSRemotingProtocolVersion: 2.3
|
||||
SerializationVersion: 1.1.0.1
|
||||
**********************
|
||||
SAP NCo CLI
|
||||
Architecture : x86
|
||||
NCo Assembly : sapnco, Version=3.1.0.42, Culture=neutral, PublicKeyToken=50436dca5c7f7d23
|
||||
Password prompt: [masked input omitted]
|
||||
Target : travt762.sap.trafag.com / SYSNR 00 / CLIENT 100 / USER KOI
|
||||
Ping : OK
|
||||
|
||||
Program : Z_TEST3
|
||||
Lines : 69
|
||||
Activation : RPY_PROGRAM_INSERT with SAVE_INACTIVE blank
|
||||
Dry run : no SAP repository changes were written.
|
||||
|
||||
Exit code: 0
|
||||
**********************
|
||||
Ende der Windows PowerShell-Aufzeichnung
|
||||
Endzeit: 20260427082529
|
||||
**********************
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
|
||||
Sage SQL CSV export
|
||||
|
||||
===================
|
||||
|
||||
Server instance: localhost
|
||||
|
||||
Database filter: (all accessible user databases)
|
||||
|
||||
From date: 2025-01-01
|
||||
|
||||
To date: 2026-01-01
|
||||
|
||||
Files:
|
||||
|
||||
- candidate_objects.csv: SQL tables/views that look relevant for sales/invoices.
|
||||
|
||||
- export_summary.csv: export status and row counts.
|
||||
|
||||
- *.csv: exported samples or selected full exports.
|
||||
|
||||
Recommended workflow:
|
||||
|
||||
1. Run discovery first:
|
||||
|
||||
.\Export-SageSqlCsv.ps1 -DiscoverOnly
|
||||
|
||||
2. Send candidate_objects.csv to Trafag/IT for selection.
|
||||
|
||||
3. Export selected objects:
|
||||
|
||||
.\Export-SageSqlCsv.ps1 -Database "DATABASE_NAME" -ObjectName "schema.table_or_view"
|
||||
|
||||
4. If the selected object is very large, add:
|
||||
|
||||
-FromDate "2025-01-01" -ToDate "2026-01-01" -MaxRowsPerObject 100000
|
||||
|
||||
The script only reads data. It does not change SQL Server or Sage.
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,16 @@
|
||||
$scriptPath = Join-Path $PSScriptRoot "Export-SageSqlCsv.ps1"
|
||||
|
||||
& $scriptPath `
|
||||
-Database "Sage" `
|
||||
-ObjectName @(
|
||||
"dbo.CabeceraAlbaranCliente",
|
||||
"dbo.LineasAlbaranCliente",
|
||||
"dbo.EstadisVenta",
|
||||
"dbo.EstadisVentaTallas",
|
||||
"dbo.FacturasTB",
|
||||
"dbo.MovimientosFacturas",
|
||||
"dbo.Vis_RTDV_EfectosFactura"
|
||||
) `
|
||||
-FromDate "2025-01-01" `
|
||||
-ToDate "2026-01-01" `
|
||||
-MaxRowsPerObject 10000
|
||||
@@ -0,0 +1,15 @@
|
||||
$scriptPath = Join-Path $PSScriptRoot "Export-SageSqlCsv.ps1"
|
||||
|
||||
& $scriptPath `
|
||||
-Database "Sage" `
|
||||
-ObjectName @(
|
||||
"dbo.CabeceraAlbaranCliente",
|
||||
"dbo.LineasAlbaranCliente",
|
||||
"dbo.EstadisVenta",
|
||||
"dbo.EstadisVentaTallas",
|
||||
"dbo.FacturasTB",
|
||||
"dbo.MovimientosFacturas",
|
||||
"dbo.Vis_RTDV_EfectosFactura"
|
||||
) `
|
||||
-FromDate "2025-01-01" `
|
||||
-ToDate "2026-01-01"
|
||||
@@ -0,0 +1,410 @@
|
||||
param(
|
||||
[string]$ServerInstance = "localhost",
|
||||
[string]$Database = "",
|
||||
[string[]]$ObjectName = @(),
|
||||
[datetime]$FromDate = "2025-01-01",
|
||||
[datetime]$ToDate = "2026-01-01",
|
||||
[string]$OutputDirectory = (Join-Path $env:USERPROFILE "Desktop"),
|
||||
[int]$SampleRows = 500,
|
||||
[int]$MaxRowsPerObject = 0,
|
||||
[switch]$DiscoverOnly,
|
||||
[switch]$ExportCandidates,
|
||||
[switch]$IncludeSystemDatabases
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function New-Connection {
|
||||
param([string]$DbName)
|
||||
|
||||
$builder = New-Object System.Data.SqlClient.SqlConnectionStringBuilder
|
||||
$builder["Data Source"] = $ServerInstance
|
||||
$builder["Initial Catalog"] = $DbName
|
||||
$builder["Integrated Security"] = $true
|
||||
$builder["TrustServerCertificate"] = $true
|
||||
$builder["Connect Timeout"] = 15
|
||||
return New-Object System.Data.SqlClient.SqlConnection($builder.ConnectionString)
|
||||
}
|
||||
|
||||
function Invoke-DataTable {
|
||||
param(
|
||||
[string]$DbName,
|
||||
[string]$Sql,
|
||||
[hashtable]$Parameters = @{}
|
||||
)
|
||||
|
||||
$conn = New-Connection $DbName
|
||||
$cmd = $conn.CreateCommand()
|
||||
$cmd.CommandText = $Sql
|
||||
$cmd.CommandTimeout = 300
|
||||
|
||||
foreach ($key in $Parameters.Keys) {
|
||||
$param = $cmd.Parameters.Add("@$key", [System.Data.SqlDbType]::NVarChar, 4000)
|
||||
$param.Value = [string]$Parameters[$key]
|
||||
}
|
||||
|
||||
$table = New-Object System.Data.DataTable
|
||||
try {
|
||||
$conn.Open()
|
||||
$reader = $cmd.ExecuteReader()
|
||||
$table.Load($reader)
|
||||
}
|
||||
finally {
|
||||
$conn.Dispose()
|
||||
}
|
||||
|
||||
return $table
|
||||
}
|
||||
|
||||
function Convert-ToCsvValue {
|
||||
param($Value)
|
||||
|
||||
if ($null -eq $Value -or $Value -is [System.DBNull]) {
|
||||
return ""
|
||||
}
|
||||
|
||||
if ($Value -is [datetime]) {
|
||||
$text = $Value.ToString("yyyy-MM-dd HH:mm:ss")
|
||||
}
|
||||
else {
|
||||
$text = [string]$Value
|
||||
}
|
||||
|
||||
$text = $text.Replace('"', '""')
|
||||
return '"' + $text + '"'
|
||||
}
|
||||
|
||||
function Export-QueryToCsv {
|
||||
param(
|
||||
[string]$DbName,
|
||||
[string]$Sql,
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
$conn = New-Connection $DbName
|
||||
$cmd = $conn.CreateCommand()
|
||||
$cmd.CommandText = $Sql
|
||||
$cmd.CommandTimeout = 0
|
||||
|
||||
$writer = New-Object System.IO.StreamWriter($Path, $false, [System.Text.Encoding]::UTF8)
|
||||
$rowCount = 0
|
||||
|
||||
try {
|
||||
$conn.Open()
|
||||
$reader = $cmd.ExecuteReader()
|
||||
|
||||
$headers = for ($i = 0; $i -lt $reader.FieldCount; $i++) {
|
||||
Convert-ToCsvValue $reader.GetName($i)
|
||||
}
|
||||
$writer.WriteLine(($headers -join ";"))
|
||||
|
||||
while ($reader.Read()) {
|
||||
$values = for ($i = 0; $i -lt $reader.FieldCount; $i++) {
|
||||
Convert-ToCsvValue $reader.GetValue($i)
|
||||
}
|
||||
$writer.WriteLine(($values -join ";"))
|
||||
$rowCount++
|
||||
}
|
||||
}
|
||||
finally {
|
||||
$writer.Dispose()
|
||||
$conn.Dispose()
|
||||
}
|
||||
|
||||
return $rowCount
|
||||
}
|
||||
|
||||
function Quote-NamePart {
|
||||
param([string]$Name)
|
||||
|
||||
return "[" + $Name.Replace("]", "]]") + "]"
|
||||
}
|
||||
|
||||
function Split-SqlObjectName {
|
||||
param([string]$Name)
|
||||
|
||||
$parts = $Name.Split(".", 2)
|
||||
if ($parts.Count -eq 1) {
|
||||
return [pscustomobject]@{ SchemaName = "dbo"; ObjectName = $parts[0] }
|
||||
}
|
||||
|
||||
return [pscustomobject]@{ SchemaName = $parts[0].Trim("[", "]"); ObjectName = $parts[1].Trim("[", "]") }
|
||||
}
|
||||
|
||||
function Get-UserDatabases {
|
||||
$sql = @"
|
||||
SELECT name
|
||||
FROM sys.databases
|
||||
WHERE state_desc = 'ONLINE'
|
||||
AND HAS_DBACCESS(name) = 1
|
||||
$(if ($IncludeSystemDatabases) { "" } else { "AND database_id > 4" })
|
||||
ORDER BY name;
|
||||
"@
|
||||
|
||||
Invoke-DataTable "master" $sql | ForEach-Object { $_.name }
|
||||
}
|
||||
|
||||
function Get-CandidateObjects {
|
||||
param([string]$DbName)
|
||||
|
||||
$sql = @"
|
||||
WITH object_columns AS (
|
||||
SELECT
|
||||
s.name AS SchemaName,
|
||||
o.name AS ObjectName,
|
||||
o.type_desc AS ObjectType,
|
||||
c.name AS ColumnName,
|
||||
t.name AS TypeName,
|
||||
c.max_length,
|
||||
c.precision,
|
||||
c.scale
|
||||
FROM sys.objects o
|
||||
JOIN sys.schemas s ON s.schema_id = o.schema_id
|
||||
JOIN sys.columns c ON c.object_id = o.object_id
|
||||
JOIN sys.types t ON t.user_type_id = c.user_type_id
|
||||
WHERE o.type IN ('U', 'V')
|
||||
AND o.is_ms_shipped = 0
|
||||
),
|
||||
scored AS (
|
||||
SELECT
|
||||
SchemaName,
|
||||
ObjectName,
|
||||
ObjectType,
|
||||
SUM(CASE WHEN LOWER(ObjectName) LIKE '%fact%' OR LOWER(ObjectName) LIKE '%invoice%' OR LOWER(ObjectName) LIKE '%venta%' OR LOWER(ObjectName) LIKE '%sales%' OR LOWER(ObjectName) LIKE '%albar%' OR LOWER(ObjectName) LIKE '%pedido%' THEN 5 ELSE 0 END) +
|
||||
SUM(CASE WHEN LOWER(ColumnName) LIKE '%fecha%' OR LOWER(ColumnName) LIKE '%date%' THEN 2 ELSE 0 END) +
|
||||
SUM(CASE WHEN LOWER(ColumnName) LIKE '%cliente%' OR LOWER(ColumnName) LIKE '%customer%' THEN 2 ELSE 0 END) +
|
||||
SUM(CASE WHEN LOWER(ColumnName) LIKE '%articulo%' OR LOWER(ColumnName) LIKE '%item%' OR LOWER(ColumnName) LIKE '%producto%' THEN 2 ELSE 0 END) +
|
||||
SUM(CASE WHEN LOWER(ColumnName) LIKE '%importe%' OR LOWER(ColumnName) LIKE '%neto%' OR LOWER(ColumnName) LIKE '%total%' OR LOWER(ColumnName) LIKE '%amount%' THEN 3 ELSE 0 END) +
|
||||
SUM(CASE WHEN LOWER(ColumnName) LIKE '%cantidad%' OR LOWER(ColumnName) LIKE '%quantity%' OR LOWER(ColumnName) LIKE '%unidades%' THEN 2 ELSE 0 END) AS Score,
|
||||
COUNT(*) AS ColumnCount,
|
||||
STRING_AGG(CONVERT(nvarchar(max), ColumnName), ', ') WITHIN GROUP (ORDER BY ColumnName) AS Columns
|
||||
FROM object_columns
|
||||
GROUP BY SchemaName, ObjectName, ObjectType
|
||||
)
|
||||
SELECT TOP (80)
|
||||
DB_NAME() AS DatabaseName,
|
||||
SchemaName,
|
||||
ObjectName,
|
||||
ObjectType,
|
||||
Score,
|
||||
ColumnCount,
|
||||
Columns
|
||||
FROM scored
|
||||
WHERE Score > 0
|
||||
ORDER BY Score DESC, ObjectName;
|
||||
"@
|
||||
|
||||
Invoke-DataTable $DbName $sql
|
||||
}
|
||||
|
||||
function Get-DateColumns {
|
||||
param(
|
||||
[string]$DbName,
|
||||
[string]$SchemaName,
|
||||
[string]$ObjectNameValue
|
||||
)
|
||||
|
||||
$sql = @"
|
||||
SELECT c.name AS ColumnName
|
||||
FROM sys.objects o
|
||||
JOIN sys.schemas s ON s.schema_id = o.schema_id
|
||||
JOIN sys.columns c ON c.object_id = o.object_id
|
||||
JOIN sys.types t ON t.user_type_id = c.user_type_id
|
||||
WHERE s.name = @schema
|
||||
AND o.name = @object
|
||||
AND (
|
||||
t.name IN ('date', 'datetime', 'datetime2', 'smalldatetime')
|
||||
OR LOWER(c.name) LIKE '%fecha%'
|
||||
OR LOWER(c.name) LIKE '%date%'
|
||||
)
|
||||
ORDER BY
|
||||
CASE
|
||||
WHEN LOWER(c.name) LIKE '%fact%' OR LOWER(c.name) LIKE '%invoice%' THEN 0
|
||||
WHEN LOWER(c.name) LIKE '%fecha%' OR LOWER(c.name) LIKE '%date%' THEN 1
|
||||
ELSE 2
|
||||
END,
|
||||
c.column_id;
|
||||
"@
|
||||
|
||||
Invoke-DataTable $DbName $sql @{ schema = $SchemaName; object = $ObjectNameValue } |
|
||||
ForEach-Object { $_.ColumnName }
|
||||
}
|
||||
|
||||
function Build-SelectSql {
|
||||
param(
|
||||
[string]$SchemaName,
|
||||
[string]$ObjectNameValue,
|
||||
[string]$DateColumn,
|
||||
[int]$TopRows
|
||||
)
|
||||
|
||||
$topClause = if ($TopRows -gt 0) { "TOP ($TopRows)" } else { "" }
|
||||
$qualified = "$(Quote-NamePart $SchemaName).$(Quote-NamePart $ObjectNameValue)"
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($DateColumn)) {
|
||||
return "SELECT $topClause * FROM $qualified;"
|
||||
}
|
||||
|
||||
$from = $FromDate.ToString("yyyy-MM-dd")
|
||||
$to = $ToDate.ToString("yyyy-MM-dd")
|
||||
$dateColumnSql = Quote-NamePart $DateColumn
|
||||
|
||||
return @"
|
||||
SELECT $topClause *
|
||||
FROM $qualified
|
||||
WHERE TRY_CONVERT(date, $dateColumnSql) >= CONVERT(date, '$from')
|
||||
AND TRY_CONVERT(date, $dateColumnSql) < CONVERT(date, '$to')
|
||||
ORDER BY TRY_CONVERT(date, $dateColumnSql);
|
||||
"@
|
||||
}
|
||||
|
||||
function Normalize-FileName {
|
||||
param([string]$Value)
|
||||
|
||||
return ($Value -replace '[\\/:*?"<>|]', '_')
|
||||
}
|
||||
|
||||
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
||||
$runDirectory = Join-Path $OutputDirectory "Sage_SQL_CSV_Export_$timestamp"
|
||||
New-Item -ItemType Directory -Path $runDirectory -Force | Out-Null
|
||||
|
||||
$databases = if ([string]::IsNullOrWhiteSpace($Database)) {
|
||||
@(Get-UserDatabases)
|
||||
}
|
||||
else {
|
||||
@($Database)
|
||||
}
|
||||
|
||||
$summary = New-Object System.Collections.Generic.List[object]
|
||||
$allCandidates = New-Object System.Collections.Generic.List[object]
|
||||
|
||||
foreach ($db in $databases) {
|
||||
Write-Host "Scanning database: $db"
|
||||
try {
|
||||
$candidates = @(Get-CandidateObjects $db)
|
||||
foreach ($candidate in $candidates) {
|
||||
$allCandidates.Add($candidate)
|
||||
}
|
||||
}
|
||||
catch {
|
||||
$summary.Add([pscustomobject]@{
|
||||
Database = $db
|
||||
Object = ""
|
||||
Action = "Discovery failed"
|
||||
Rows = 0
|
||||
File = ""
|
||||
Error = $_.Exception.Message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
$candidatePath = Join-Path $runDirectory "candidate_objects.csv"
|
||||
if ($allCandidates.Count -gt 0) {
|
||||
$allCandidates | Export-Csv -LiteralPath $candidatePath -NoTypeInformation -Encoding UTF8 -Delimiter ";"
|
||||
}
|
||||
|
||||
if (-not $DiscoverOnly) {
|
||||
$objectsToExport = New-Object System.Collections.Generic.List[object]
|
||||
|
||||
foreach ($name in $ObjectName) {
|
||||
if ([string]::IsNullOrWhiteSpace($name)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Database)) {
|
||||
throw "When -ObjectName is used, pass -Database as well."
|
||||
}
|
||||
|
||||
$parsed = Split-SqlObjectName $name
|
||||
$objectsToExport.Add([pscustomobject]@{
|
||||
DatabaseName = $Database
|
||||
SchemaName = $parsed.SchemaName
|
||||
ObjectName = $parsed.ObjectName
|
||||
})
|
||||
}
|
||||
|
||||
if ($ExportCandidates) {
|
||||
foreach ($candidate in ($allCandidates | Sort-Object DatabaseName, @{Expression="Score"; Descending=$true} | Select-Object -First 25)) {
|
||||
$objectsToExport.Add([pscustomobject]@{
|
||||
DatabaseName = $candidate.DatabaseName
|
||||
SchemaName = $candidate.SchemaName
|
||||
ObjectName = $candidate.ObjectName
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($object in $objectsToExport) {
|
||||
$db = $object.DatabaseName
|
||||
$schema = $object.SchemaName
|
||||
$objectNameValue = $object.ObjectName
|
||||
|
||||
try {
|
||||
$dateColumn = @(Get-DateColumns $db $schema $objectNameValue | Select-Object -First 1)[0]
|
||||
$limit = if ($MaxRowsPerObject -gt 0) { $MaxRowsPerObject } elseif ($ObjectName.Count -gt 0) { 0 } else { $SampleRows }
|
||||
$sql = Build-SelectSql $schema $objectNameValue $dateColumn $limit
|
||||
$fileName = Normalize-FileName "$db.$schema.$objectNameValue.csv"
|
||||
$path = Join-Path $runDirectory $fileName
|
||||
Write-Host "Exporting $db.$schema.$objectNameValue -> $path"
|
||||
$rows = Export-QueryToCsv $db $sql $path
|
||||
|
||||
$summary.Add([pscustomobject]@{
|
||||
Database = $db
|
||||
Object = "$schema.$objectNameValue"
|
||||
Action = "Exported"
|
||||
Rows = $rows
|
||||
File = $path
|
||||
DateColumn = $dateColumn
|
||||
Error = ""
|
||||
})
|
||||
}
|
||||
catch {
|
||||
$summary.Add([pscustomobject]@{
|
||||
Database = $db
|
||||
Object = "$schema.$objectNameValue"
|
||||
Action = "Export failed"
|
||||
Rows = 0
|
||||
File = ""
|
||||
DateColumn = ""
|
||||
Error = $_.Exception.Message
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$summaryPath = Join-Path $runDirectory "export_summary.csv"
|
||||
$summary | Export-Csv -LiteralPath $summaryPath -NoTypeInformation -Encoding UTF8 -Delimiter ";"
|
||||
|
||||
$readmePath = Join-Path $runDirectory "README.txt"
|
||||
@"
|
||||
Sage SQL CSV export
|
||||
===================
|
||||
|
||||
Server instance: $ServerInstance
|
||||
Database filter: $(if ($Database) { $Database } else { "(all accessible user databases)" })
|
||||
From date: $($FromDate.ToString("yyyy-MM-dd"))
|
||||
To date: $($ToDate.ToString("yyyy-MM-dd"))
|
||||
|
||||
Files:
|
||||
- candidate_objects.csv: SQL tables/views that look relevant for sales/invoices.
|
||||
- export_summary.csv: export status and row counts.
|
||||
- *.csv: exported samples or selected full exports.
|
||||
|
||||
Recommended workflow:
|
||||
1. Run discovery first:
|
||||
.\Export-SageSqlCsv.ps1 -DiscoverOnly
|
||||
2. Send candidate_objects.csv to Trafag/IT for selection.
|
||||
3. Export selected objects:
|
||||
.\Export-SageSqlCsv.ps1 -Database "DATABASE_NAME" -ObjectName "schema.table_or_view"
|
||||
4. If the selected object is very large, add:
|
||||
-FromDate "2025-01-01" -ToDate "2026-01-01" -MaxRowsPerObject 100000
|
||||
|
||||
The script only reads data. It does not change SQL Server or Sage.
|
||||
"@ | Set-Content -LiteralPath $readmePath -Encoding UTF8
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Created folder:"
|
||||
Write-Host " $runDirectory"
|
||||
Write-Host ""
|
||||
Write-Host "Main files:"
|
||||
Write-Host " $candidatePath"
|
||||
Write-Host " $summaryPath"
|
||||
@@ -0,0 +1,23 @@
|
||||
Sage SQL CSV export
|
||||
===================
|
||||
|
||||
Server instance: localhost
|
||||
Database filter: Sage
|
||||
From date: 2025-01-01
|
||||
To date: 2026-01-01
|
||||
|
||||
Files:
|
||||
- candidate_objects.csv: SQL tables/views that look relevant for sales/invoices.
|
||||
- export_summary.csv: export status and row counts.
|
||||
- *.csv: exported samples or selected full exports.
|
||||
|
||||
Recommended workflow:
|
||||
1. Run discovery first:
|
||||
.\Export-SageSqlCsv.ps1 -DiscoverOnly
|
||||
2. Send candidate_objects.csv to Trafag/IT for selection.
|
||||
3. Export selected objects:
|
||||
.\Export-SageSqlCsv.ps1 -Database "DATABASE_NAME" -ObjectName "schema.table_or_view"
|
||||
4. If the selected object is very large, add:
|
||||
-FromDate "2025-01-01" -ToDate "2026-01-01" -MaxRowsPerObject 100000
|
||||
|
||||
The script only reads data. It does not change SQL Server or Sage.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
"CodigoEmpresa";"EstadisClave1";"EstadisClave2";"EstadisClave3";"Ejercicio";"Periodo";"Origen";"CodigoZona";"CodigoJefeZona_";"CodigoJefeVenta_";"CodigoComisionista";"CodigoComisionista2_";"CodigoComisionista3_";"CodigoComisionista4_";"CodigoCliente";"CodigoFamilia";"CodigoSubfamilia";"CodigoArticulo";"CodigoColor_";"GrupoTalla_";"UnidadesTalla01_";"UnidadesTalla02_";"UnidadesTalla03_";"UnidadesTalla04_";"UnidadesTalla05_";"UnidadesTalla06_";"UnidadesTalla07_";"UnidadesTalla08_";"UnidadesTalla09_";"UnidadesTalla10_";"UnidadesTalla11_";"UnidadesTalla12_";"UnidadesTalla13_";"UnidadesTalla14_";"UnidadesTalla15_";"UnidadesTalla16_";"UnidadesTalla17_";"UnidadesTalla18_";"UnidadesTalla19_";"UnidadesTalla20_";"UnidadesTalla21_";"UnidadesTalla22_";"UnidadesTalla23_";"UnidadesTalla24_";"UnidadesTalla25_";"UnidadesTalla26_";"UnidadesTalla27_";"UnidadesTalla28_";"UnidadesTalla29_";"UnidadesTalla30_";"UnidadesTalla31_";"UnidadesTalla32_";"UnidadesTalla33_";"UnidadesTalla34_";"UnidadesTalla35_";"UnidadesTalla36_";"UnidadesTalla37_";"UnidadesTalla38_";"UnidadesTalla39_";"UnidadesTalla40_";"ImporteTalla01_";"ImporteTalla02_";"ImporteTalla03_";"ImporteTalla04_";"ImporteTalla05_";"ImporteTalla06_";"ImporteTalla07_";"ImporteTalla08_";"ImporteTalla09_";"ImporteTalla10_";"ImporteTalla11_";"ImporteTalla12_";"ImporteTalla13_";"ImporteTalla14_";"ImporteTalla15_";"ImporteTalla16_";"ImporteTalla17_";"ImporteTalla18_";"ImporteTalla19_";"ImporteTalla20_";"ImporteTalla21_";"ImporteTalla22_";"ImporteTalla23_";"ImporteTalla24_";"ImporteTalla25_";"ImporteTalla26_";"ImporteTalla27_";"ImporteTalla28_";"ImporteTalla29_";"ImporteTalla30_";"ImporteTalla31_";"ImporteTalla32_";"ImporteTalla33_";"ImporteTalla34_";"ImporteTalla35_";"ImporteTalla36_";"ImporteTalla37_";"ImporteTalla38_";"ImporteTalla39_";"ImporteTalla40_";"UnidadesTotalTallas_";"ImporteTotalTallas_"
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
"oppCoId";"effeForecast";"effeNumber";"invoExercise";"invoSeries";"invoNumber";"effeOrder";"statusDelete";"customerCode";"customer";"effeAmount";"expirationDate";"invoDate";"emissionDate";"accountCode";"counterPart";"comment";"canalCode";"statusRemitted";"remittedType";"remittedDate";"remittedBank";"remittedNumber";"statusRisk";"statusUnpaid";"salesPersonId";"salesPerson";"effectType";"effectClass";"effeId";"invoId"
|
||||
|
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
||||
"Database";"Object";"Action";"Rows";"File";"DateColumn";"Error"
|
||||
"Sage";"dbo.CabeceraAlbaranCliente";"Exported";"1973";"C:\Users\Administrador\Desktop\Sage_SQL_CSV_Export_20260505_103719\Sage.dbo.CabeceraAlbaranCliente.csv";"FechaFactura";""
|
||||
"Sage";"dbo.LineasAlbaranCliente";"Exported";"4814";"C:\Users\Administrador\Desktop\Sage_SQL_CSV_Export_20260505_103719\Sage.dbo.LineasAlbaranCliente.csv";"FechaRegistro";""
|
||||
"Sage";"dbo.EstadisVenta";"Exported";"16976";"C:\Users\Administrador\Desktop\Sage_SQL_CSV_Export_20260505_103719\Sage.dbo.EstadisVenta.csv";;""
|
||||
"Sage";"dbo.EstadisVentaTallas";"Exported";"0";"C:\Users\Administrador\Desktop\Sage_SQL_CSV_Export_20260505_103719\Sage.dbo.EstadisVentaTallas.csv";;""
|
||||
"Sage";"dbo.FacturasTB";"Exported";"3788";"C:\Users\Administrador\Desktop\Sage_SQL_CSV_Export_20260505_103719\Sage.dbo.FacturasTB.csv";"FechaFactura";""
|
||||
"Sage";"dbo.MovimientosFacturas";"Exported";"6517";"C:\Users\Administrador\Desktop\Sage_SQL_CSV_Export_20260505_103719\Sage.dbo.MovimientosFacturas.csv";"FechaFactura";""
|
||||
"Sage";"dbo.Vis_RTDV_EfectosFactura";"Exported";"0";"C:\Users\Administrador\Desktop\Sage_SQL_CSV_Export_20260505_103719\Sage.dbo.Vis_RTDV_EfectosFactura.csv";"expirationDate";""
|
||||
|
Binary file not shown.
@@ -0,0 +1,215 @@
|
||||
param(
|
||||
[string]$ServerInstance = "localhost",
|
||||
[string]$Database = "Sage",
|
||||
[datetime]$FromDate = "2025-01-01",
|
||||
[datetime]$ToDate = "2026-01-01",
|
||||
[string]$OutputDirectory = (Join-Path $env:USERPROFILE "Desktop")
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function New-Connection {
|
||||
$builder = New-Object System.Data.SqlClient.SqlConnectionStringBuilder
|
||||
$builder["Data Source"] = $ServerInstance
|
||||
$builder["Initial Catalog"] = $Database
|
||||
$builder["Integrated Security"] = $true
|
||||
$builder["TrustServerCertificate"] = $true
|
||||
$builder["Connect Timeout"] = 15
|
||||
return New-Object System.Data.SqlClient.SqlConnection($builder.ConnectionString)
|
||||
}
|
||||
|
||||
function Convert-ToCsvValue {
|
||||
param($Value)
|
||||
|
||||
if ($null -eq $Value -or $Value -is [System.DBNull]) {
|
||||
return ""
|
||||
}
|
||||
|
||||
if ($Value -is [datetime]) {
|
||||
$text = $Value.ToString("yyyy-MM-dd HH:mm:ss")
|
||||
}
|
||||
else {
|
||||
$text = [string]$Value
|
||||
}
|
||||
|
||||
$text = $text.Replace('"', '""')
|
||||
return '"' + $text + '"'
|
||||
}
|
||||
|
||||
function Export-QueryToCsv {
|
||||
param(
|
||||
[string]$Sql,
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
$conn = New-Connection
|
||||
$cmd = $conn.CreateCommand()
|
||||
$cmd.CommandText = $Sql
|
||||
$cmd.CommandTimeout = 0
|
||||
|
||||
$fromParameter = $cmd.Parameters.Add("@FromDate", [System.Data.SqlDbType]::Date)
|
||||
$fromParameter.Value = $FromDate.Date
|
||||
|
||||
$toParameter = $cmd.Parameters.Add("@ToDate", [System.Data.SqlDbType]::Date)
|
||||
$toParameter.Value = $ToDate.Date
|
||||
|
||||
$writer = New-Object System.IO.StreamWriter($Path, $false, [System.Text.Encoding]::UTF8)
|
||||
$rowCount = 0
|
||||
$salesSum = [decimal]0
|
||||
|
||||
try {
|
||||
$conn.Open()
|
||||
$reader = $cmd.ExecuteReader()
|
||||
|
||||
$headers = for ($i = 0; $i -lt $reader.FieldCount; $i++) {
|
||||
Convert-ToCsvValue $reader.GetName($i)
|
||||
}
|
||||
$writer.WriteLine(($headers -join ";"))
|
||||
|
||||
$salesIndex = -1
|
||||
for ($i = 0; $i -lt $reader.FieldCount; $i++) {
|
||||
if ($reader.GetName($i) -eq "SalesPriceValue") {
|
||||
$salesIndex = $i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
while ($reader.Read()) {
|
||||
$values = for ($i = 0; $i -lt $reader.FieldCount; $i++) {
|
||||
Convert-ToCsvValue $reader.GetValue($i)
|
||||
}
|
||||
$writer.WriteLine(($values -join ";"))
|
||||
$rowCount++
|
||||
|
||||
if ($salesIndex -ge 0 -and -not $reader.IsDBNull($salesIndex)) {
|
||||
$salesSum += [decimal]$reader.GetValue($salesIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
$writer.Dispose()
|
||||
$conn.Dispose()
|
||||
}
|
||||
|
||||
return [pscustomobject]@{
|
||||
Rows = $rowCount
|
||||
SalesPriceValueSum = $salesSum
|
||||
}
|
||||
}
|
||||
|
||||
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
||||
$runDirectory = Join-Path $OutputDirectory "Sage_Spain_Sales_Export_$timestamp"
|
||||
New-Item -ItemType Directory -Path $runDirectory -Force | Out-Null
|
||||
|
||||
$csvPath = Join-Path $runDirectory "Spain_Sales_2025.csv"
|
||||
$summaryPath = Join-Path $runDirectory "Spain_Sales_2025_summary.txt"
|
||||
|
||||
$sql = @"
|
||||
SELECT
|
||||
'TRES' AS TSC,
|
||||
'Spanien' AS Land,
|
||||
'Sage' AS SourceSystem,
|
||||
c.CodigoEmpresa AS CompanyCode,
|
||||
c.EjercicioAlbaran AS DeliveryYear,
|
||||
c.SerieAlbaran AS DeliverySeries,
|
||||
c.NumeroAlbaran AS DeliveryNumber,
|
||||
c.EjercicioFactura AS InvoiceYear,
|
||||
c.SerieFactura AS InvoiceSeries,
|
||||
c.NumeroFactura AS InvoiceNumber,
|
||||
l.Orden AS PositionOnInvoice,
|
||||
l.LineasPosicion AS SourceLineId,
|
||||
l.CodigoArticulo AS Material,
|
||||
l.DescripcionArticulo AS Name,
|
||||
l.Descripcion2Articulo AS Description2,
|
||||
l.DescripcionLinea AS DescriptionLine,
|
||||
l.CodigoFamilia AS ProductGroup,
|
||||
l.CodigoSubfamilia AS ProductSubGroup,
|
||||
CAST(l.Unidades AS decimal(19, 6)) AS Quantity,
|
||||
c.CodigoCliente AS CustomerNumber,
|
||||
c.Nombre AS CustomerName,
|
||||
c.CodigoNacion AS CustomerCountryCode,
|
||||
c.Nacion AS CustomerCountry,
|
||||
CAST(l.PrecioCoste AS decimal(19, 6)) AS StandardCost,
|
||||
CAST(l.ImporteCoste AS decimal(19, 6)) AS StandardCostValue,
|
||||
'EUR' AS StandardCostCurrency,
|
||||
CAST(l.ImporteNeto AS decimal(19, 6)) AS SalesPriceValue,
|
||||
'EUR' AS SalesCurrency,
|
||||
'EUR' AS DocumentCurrency,
|
||||
'EUR' AS CompanyCurrency,
|
||||
c.CodigoDivisa AS SageCurrencyCode,
|
||||
CAST(c.BaseImponible AS decimal(19, 6)) AS DocumentNetAmount,
|
||||
CAST(c.TotalIva AS decimal(19, 6)) AS DocumentVatAmount,
|
||||
CAST(c.ImporteFactura AS decimal(19, 6)) AS DocumentGrossAmount,
|
||||
c.FechaFactura AS InvoiceDate,
|
||||
c.FechaAlbaran AS DeliveryDate,
|
||||
l.FechaRegistro AS LineRegistrationDate,
|
||||
c.EjercicioPedido AS OrderYear,
|
||||
c.SeriePedido AS OrderSeries,
|
||||
c.NumeroPedido AS OrderNumber,
|
||||
c.SuPedido AS PurchaseOrderNumber,
|
||||
c.CodigoExportacion_ AS Incoterms2020,
|
||||
c.CondicionExportacion_ AS IncotermsText,
|
||||
c.CodigoComisionista AS SalesResponsibleEmployee,
|
||||
c.StatusAbono AS CreditStatus,
|
||||
c.NoFacturable AS NonBillable,
|
||||
c.TipoNuevaFra AS InvoiceType,
|
||||
c.StatusFacturado AS BillingStatus,
|
||||
CASE
|
||||
WHEN c.TipoNuevaFra = 2 OR c.SerieFactura = 'REC' OR c.StatusAbono <> 0 THEN 'Credit Note'
|
||||
ELSE 'Invoice'
|
||||
END AS DocumentType
|
||||
FROM dbo.CabeceraAlbaranCliente c
|
||||
JOIN dbo.LineasAlbaranCliente l
|
||||
ON l.CodigoEmpresa = c.CodigoEmpresa
|
||||
AND l.EjercicioAlbaran = c.EjercicioAlbaran
|
||||
AND l.SerieAlbaran = c.SerieAlbaran
|
||||
AND l.NumeroAlbaran = c.NumeroAlbaran
|
||||
WHERE c.FechaFactura >= @FromDate
|
||||
AND c.FechaFactura < @ToDate
|
||||
ORDER BY
|
||||
c.FechaFactura,
|
||||
c.SerieFactura,
|
||||
c.NumeroFactura,
|
||||
l.Orden;
|
||||
"@
|
||||
|
||||
$result = Export-QueryToCsv -Sql $sql -Path $csvPath
|
||||
|
||||
@"
|
||||
Sage Spain Sales CSV export
|
||||
===========================
|
||||
|
||||
Created: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
|
||||
Server instance: $ServerInstance
|
||||
Database: $Database
|
||||
From date: $($FromDate.ToString("yyyy-MM-dd"))
|
||||
To date: $($ToDate.ToString("yyyy-MM-dd"))
|
||||
|
||||
Output:
|
||||
$csvPath
|
||||
|
||||
Rows:
|
||||
$($result.Rows)
|
||||
|
||||
SalesPriceValue sum:
|
||||
$($result.SalesPriceValueSum)
|
||||
|
||||
Source:
|
||||
dbo.CabeceraAlbaranCliente joined with dbo.LineasAlbaranCliente
|
||||
|
||||
Filter:
|
||||
CabeceraAlbaranCliente.FechaFactura >= FromDate
|
||||
CabeceraAlbaranCliente.FechaFactura < ToDate
|
||||
|
||||
Notes:
|
||||
- Currency is set to EUR because Sage exports EnEuros_=-1 and CodigoDivisa is empty in the analysed rows.
|
||||
- SalesPriceValue uses LineasAlbaranCliente.ImporteNeto.
|
||||
- DocumentNetAmount uses CabeceraAlbaranCliente.BaseImponible.
|
||||
- Credit notes are marked when TipoNuevaFra=2, SerieFactura='REC', or StatusAbono is non-zero.
|
||||
"@ | Set-Content -LiteralPath $summaryPath -Encoding UTF8
|
||||
|
||||
Write-Host "Created:"
|
||||
Write-Host " $csvPath"
|
||||
Write-Host " $summaryPath"
|
||||
Write-Host "Rows: $($result.Rows)"
|
||||
Write-Host "SalesPriceValue sum: $($result.SalesPriceValueSum)"
|
||||
@@ -0,0 +1,32 @@
|
||||
Sage Spain final sales export candidate
|
||||
======================================
|
||||
|
||||
Run on the Spain Sage SQL Server machine.
|
||||
|
||||
PowerShell commands:
|
||||
|
||||
Set-ExecutionPolicy -Scope Process Bypass
|
||||
.\Export-SageSpainSalesCsv.ps1
|
||||
|
||||
Output folder on Desktop:
|
||||
|
||||
Sage_Spain_Sales_Export_YYYYMMDD_HHMMSS
|
||||
|
||||
Files created:
|
||||
|
||||
- Spain_Sales_2025.csv
|
||||
- Spain_Sales_2025_summary.txt
|
||||
|
||||
The script only reads SQL Server data. It does not change Sage or SQL Server.
|
||||
|
||||
Default source:
|
||||
|
||||
- Database: Sage
|
||||
- Header: dbo.CabeceraAlbaranCliente
|
||||
- Lines: dbo.LineasAlbaranCliente
|
||||
- Date filter: CabeceraAlbaranCliente.FechaFactura from 2025-01-01 to 2026-01-01
|
||||
- Sales value: LineasAlbaranCliente.ImporteNeto
|
||||
|
||||
If the SQL instance or database name differs:
|
||||
|
||||
.\Export-SageSpainSalesCsv.ps1 -ServerInstance "localhost" -Database "Sage"
|
||||
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"CapturedAt": "2026-05-05T10:05:13.9281781+02:00",
|
||||
"ComputerName": "WIN-4BJQJ9S1PVJ",
|
||||
"UserName": "WIN-4BJQJ9S1PVJ\\Administrador",
|
||||
"Windows": {
|
||||
"Caption": "Microsoft Windows Server 2019 Standard",
|
||||
"Version": "10.0.17763",
|
||||
"BuildNumber": "17763",
|
||||
"InstallDate": "\/Date(1601446676000)\/"
|
||||
},
|
||||
"SageUninstallEntries": [
|
||||
{
|
||||
"DisplayName": "JRE 2.5",
|
||||
"DisplayVersion": null,
|
||||
"Publisher": "Sage Logic Control",
|
||||
"InstallDate": null,
|
||||
"InstallLocation": null,
|
||||
"UninstallString": "\"C:\\Windows\\unins000.exe\"",
|
||||
"PSPath": "Microsoft.PowerShell.Core\\Registry::HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\JRE_is1"
|
||||
},
|
||||
{
|
||||
"DisplayName": "Sage 200c",
|
||||
"DisplayVersion": "2026.56.000",
|
||||
"Publisher": "Sage Spain",
|
||||
"InstallDate": null,
|
||||
"InstallLocation": null,
|
||||
"UninstallString": "C:\\Program Files (x86)\\Sage\\Sage 200c\\Setup\\Uninstall\\Sage.Uninstall.exe",
|
||||
"PSPath": "Microsoft.PowerShell.Core\\Registry::HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Sage 200c"
|
||||
},
|
||||
{
|
||||
"DisplayName": "Sage Renta Componentes",
|
||||
"DisplayVersion": "1.00.0000",
|
||||
"Publisher": "Sage Spain",
|
||||
"InstallDate": "20201021",
|
||||
"InstallLocation": "C:\\Windows\\SysWOW64\\",
|
||||
"UninstallString": "MsiExec.exe /X{0ADD979C-205B-4264-B903-6F953F362917}",
|
||||
"PSPath": "Microsoft.PowerShell.Core\\Registry::HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{0ADD979C-205B-4264-B903-6F953F362917}"
|
||||
},
|
||||
{
|
||||
"DisplayName": "Sage SGE Runtime",
|
||||
"DisplayVersion": "1.00.0000",
|
||||
"Publisher": "Sage Spain",
|
||||
"InstallDate": "20201021",
|
||||
"InstallLocation": "C:\\Windows\\SysWOW64\\",
|
||||
"UninstallString": "MsiExec.exe /X{1FFF90A6-3F93-4123-9C3B-54EBBDD22757}",
|
||||
"PSPath": "Microsoft.PowerShell.Core\\Registry::HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{1FFF90A6-3F93-4123-9C3B-54EBBDD22757}"
|
||||
},
|
||||
{
|
||||
"DisplayName": "Sage Live Update Service",
|
||||
"DisplayVersion": "1.0.8.0",
|
||||
"Publisher": "Sage",
|
||||
"InstallDate": "20230314",
|
||||
"InstallLocation": "",
|
||||
"UninstallString": "MsiExec.exe /I{6D538240-299A-47CC-8782-2062AD2F2189}",
|
||||
"PSPath": "Microsoft.PowerShell.Core\\Registry::HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{6D538240-299A-47CC-8782-2062AD2F2189}"
|
||||
},
|
||||
{
|
||||
"DisplayName": "Sage API OnPremise Service",
|
||||
"DisplayVersion": "1.2.8.0",
|
||||
"Publisher": "Sage",
|
||||
"InstallDate": "20201021",
|
||||
"InstallLocation": "",
|
||||
"UninstallString": "MsiExec.exe /I{9881C355-CB1B-4007-AB3A-B12F222318DB}",
|
||||
"PSPath": "Microsoft.PowerShell.Core\\Registry::HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{9881C355-CB1B-4007-AB3A-B12F222318DB}"
|
||||
}
|
||||
],
|
||||
"SageFileVersions": [
|
||||
|
||||
],
|
||||
"SqlRegistryInstances": [
|
||||
{
|
||||
"InstanceName": "MSSQLSERVER",
|
||||
"InstanceId": "MSSQL15.MSSQLSERVER",
|
||||
"Edition": "Standard Edition",
|
||||
"Version": "15.0.2000.5",
|
||||
"PatchLevel": "15.0.2155.2",
|
||||
"ProductCode": "{A60B3D8E-5311-4BF1-AF7A-D1AC15F9152E}",
|
||||
"SQLPath": "C:\\Program Files\\Microsoft SQL Server\\MSSQL15.MSSQLSERVER\\MSSQL",
|
||||
"SetupPath": "HKLM:\\SOFTWARE\\Microsoft\\Microsoft SQL Server\\MSSQL15.MSSQLSERVER\\Setup"
|
||||
}
|
||||
],
|
||||
"SqlServices": [
|
||||
{
|
||||
"Name": "MSSQLFDLauncher",
|
||||
"DisplayName": "SQL Full-text Filter Daemon Launcher (MSSQLSERVER)",
|
||||
"State": "Running",
|
||||
"StartMode": "Manual",
|
||||
"PathName": "\"C:\\Program Files\\Microsoft SQL Server\\MSSQL15.MSSQLSERVER\\MSSQL\\Binn\\fdlauncher.exe\" -s MSSQL15.MSSQLSERVER"
|
||||
},
|
||||
{
|
||||
"Name": "MSSQLSERVER",
|
||||
"DisplayName": "SQL Server (MSSQLSERVER)",
|
||||
"State": "Running",
|
||||
"StartMode": "Auto",
|
||||
"PathName": "\"C:\\Program Files\\Microsoft SQL Server\\MSSQL15.MSSQLSERVER\\MSSQL\\Binn\\sqlservr.exe\" -sMSSQLSERVER"
|
||||
},
|
||||
{
|
||||
"Name": "SQLBrowser",
|
||||
"DisplayName": "SQL Server Browser",
|
||||
"State": "Stopped",
|
||||
"StartMode": "Disabled",
|
||||
"PathName": "\"C:\\Program Files (x86)\\Microsoft SQL Server\\90\\Shared\\sqlbrowser.exe\""
|
||||
},
|
||||
{
|
||||
"Name": "SQLSERVERAGENT",
|
||||
"DisplayName": "Agente SQL Server (MSSQLSERVER)",
|
||||
"State": "Running",
|
||||
"StartMode": "Auto",
|
||||
"PathName": "\"C:\\Program Files\\Microsoft SQL Server\\MSSQL15.MSSQLSERVER\\MSSQL\\Binn\\SQLAGENT.EXE\" -i MSSQLSERVER"
|
||||
},
|
||||
{
|
||||
"Name": "SQLTELEMETRY",
|
||||
"DisplayName": "Servicio CEIP de SQL Server (MSSQLSERVER)",
|
||||
"State": "Running",
|
||||
"StartMode": "Auto",
|
||||
"PathName": "\"C:\\Program Files\\Microsoft SQL Server\\MSSQL15.MSSQLSERVER\\MSSQL\\Binn\\sqlceip.exe\" -Service "
|
||||
},
|
||||
{
|
||||
"Name": "SQLWriter",
|
||||
"DisplayName": "SQL Server VSS Writer",
|
||||
"State": "Running",
|
||||
"StartMode": "Auto",
|
||||
"PathName": "\"C:\\Program Files\\Microsoft SQL Server\\90\\Shared\\sqlwriter.exe\""
|
||||
}
|
||||
],
|
||||
"SqlcmdPath": "C:\\Program Files\\Microsoft SQL Server\\Client SDK\\ODBC\\170\\Tools\\Binn\\SQLCMD.EXE",
|
||||
"SqlQueryResults": [
|
||||
{
|
||||
"Instance": "localhost",
|
||||
"Success": true,
|
||||
"Output": "FullVersion|ProductVersion|ProductLevel|Edition|EngineEdition|MachineName|ServerName|InstanceName|Collation\r\n-----------|--------------|------------|-------|-------------|-----------|----------|------------|---------\r\nMicrosoft SQL Server 2019 (RTM-GDR) (KB5068405) - 15.0.2155.2 (X64) \r\n\tOct 7 2025 21:11:52 \r\n\tCopyright (C) 2019 Microsoft Corporation\r\n\tStandard Edition (64-bit) on Windows Server 2019 Standard 10.0 \u003cX64\u003e (Build 17763: ) (Hypervisor)\r\n|15.0.2155.2|RTM|Standard Edition (64-bit)|2|WIN-4BJQJ9S1PVJ|WIN-4BJQJ9S1PVJ|NULL|Latin1_General_CI_AI"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
|
||||
============================================================
|
||||
Capture metadata
|
||||
============================================================
|
||||
Timestamp: 2026-05-05 10:05:13
|
||||
Computer: WIN-4BJQJ9S1PVJ
|
||||
User: WIN-4BJQJ9S1PVJ\Administrador
|
||||
Output text: C:\Users\Administrador\Desktop\Sage_SQL_Environment_20260505_100511.txt
|
||||
Output json: C:\Users\Administrador\Desktop\Sage_SQL_Environment_20260505_100511.json
|
||||
|
||||
============================================================
|
||||
Windows / machine
|
||||
============================================================
|
||||
Manufacturer: Xen
|
||||
Model: HVM domU
|
||||
OS: Microsoft Windows Server 2019 Standard
|
||||
OS Version: 10.0.17763
|
||||
OS Build: 17763
|
||||
Install date: 09/30/2020 08:17:56
|
||||
|
||||
============================================================
|
||||
Sage entries from installed programs
|
||||
============================================================
|
||||
|
||||
|
||||
DisplayName : JRE 2.5
|
||||
DisplayVersion :
|
||||
Publisher : Sage Logic Control
|
||||
InstallDate :
|
||||
InstallLocation :
|
||||
UninstallString : "C:\Windows\unins000.exe"
|
||||
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\JRE_is1
|
||||
|
||||
DisplayName : Sage 200c
|
||||
DisplayVersion : 2026.56.000
|
||||
Publisher : Sage Spain
|
||||
InstallDate :
|
||||
InstallLocation :
|
||||
UninstallString : C:\Program Files (x86)\Sage\Sage 200c\Setup\Uninstall\Sage.Uninstall.exe
|
||||
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Sage 200c
|
||||
|
||||
DisplayName : Sage Renta Componentes
|
||||
DisplayVersion : 1.00.0000
|
||||
Publisher : Sage Spain
|
||||
InstallDate : 20201021
|
||||
InstallLocation : C:\Windows\SysWOW64\
|
||||
UninstallString : MsiExec.exe /X{0ADD979C-205B-4264-B903-6F953F362917}
|
||||
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{0ADD979C-205B-4264-B903-6F953F3
|
||||
62917}
|
||||
|
||||
DisplayName : Sage SGE Runtime
|
||||
DisplayVersion : 1.00.0000
|
||||
Publisher : Sage Spain
|
||||
InstallDate : 20201021
|
||||
InstallLocation : C:\Windows\SysWOW64\
|
||||
UninstallString : MsiExec.exe /X{1FFF90A6-3F93-4123-9C3B-54EBBDD22757}
|
||||
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{1FFF90A6-3F93-4123-9C3B-54EBBDD
|
||||
22757}
|
||||
|
||||
DisplayName : Sage Live Update Service
|
||||
DisplayVersion : 1.0.8.0
|
||||
Publisher : Sage
|
||||
InstallDate : 20230314
|
||||
InstallLocation :
|
||||
UninstallString : MsiExec.exe /I{6D538240-299A-47CC-8782-2062AD2F2189}
|
||||
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{6D538240-299A-47CC-8782-2062AD2
|
||||
F2189}
|
||||
|
||||
DisplayName : Sage API OnPremise Service
|
||||
DisplayVersion : 1.2.8.0
|
||||
Publisher : Sage
|
||||
InstallDate : 20201021
|
||||
InstallLocation :
|
||||
UninstallString : MsiExec.exe /I{9881C355-CB1B-4007-AB3A-B12F222318DB}
|
||||
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{9881C355-CB1B-4007-AB3A-B12F222
|
||||
318DB}
|
||||
|
||||
============================================================
|
||||
Sage file versions
|
||||
============================================================
|
||||
Skipped. Re-run with -ScanProgramFiles for file version scan.
|
||||
|
||||
============================================================
|
||||
SQL Server instances from registry
|
||||
============================================================
|
||||
|
||||
|
||||
InstanceName : MSSQLSERVER
|
||||
InstanceId : MSSQL15.MSSQLSERVER
|
||||
Edition : Standard Edition
|
||||
Version : 15.0.2000.5
|
||||
PatchLevel : 15.0.2155.2
|
||||
ProductCode : {A60B3D8E-5311-4BF1-AF7A-D1AC15F9152E}
|
||||
SQLPath : C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL
|
||||
SetupPath : HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL15.MSSQLSERVER\Setup
|
||||
|
||||
============================================================
|
||||
SQL Server services
|
||||
============================================================
|
||||
|
||||
Name DisplayName State StartMode PathName
|
||||
---- ----------- ----- --------- --------
|
||||
MSSQLFDLauncher SQL Full-text Filter Daemon Launcher (MSSQLSERVER) Running Manual "C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\Binn\fdlauncher....
|
||||
MSSQLSERVER SQL Server (MSSQLSERVER) Running Auto "C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\Binn\sqlservr.ex...
|
||||
SQLBrowser SQL Server Browser Stopped Disabled "C:\Program Files (x86)\Microsoft SQL Server\90\Shared\sqlbrowser.exe"
|
||||
SQLSERVERAGENT Agente SQL Server (MSSQLSERVER) Running Auto "C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\Binn\SQLAGENT.EX...
|
||||
SQLTELEMETRY Servicio CEIP de SQL Server (MSSQLSERVER) Running Auto "C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\Binn\sqlceip.exe...
|
||||
SQLWriter SQL Server VSS Writer Running Auto "C:\Program Files\Microsoft SQL Server\90\Shared\sqlwriter.exe"
|
||||
|
||||
============================================================
|
||||
SQL Server live query
|
||||
============================================================
|
||||
sqlcmd path: C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\SQLCMD.EXE
|
||||
|
||||
Instance: localhost
|
||||
Success: True
|
||||
FullVersion|ProductVersion|ProductLevel|Edition|EngineEdition|MachineName|ServerName|InstanceName|Collation
|
||||
-----------|--------------|------------|-------|-------------|-----------|----------|------------|---------
|
||||
Microsoft SQL Server 2019 (RTM-GDR) (KB5068405) - 15.0.2155.2 (X64)
|
||||
Oct 7 2025 21:11:52
|
||||
Copyright (C) 2019 Microsoft Corporation
|
||||
Standard Edition (64-bit) on Windows Server 2019 Standard 10.0 <X64> (Build 17763: ) (Hypervisor)
|
||||
|15.0.2155.2|RTM|Standard Edition (64-bit)|2|WIN-4BJQJ9S1PVJ|WIN-4BJQJ9S1PVJ|NULL|Latin1_General_CI_AI
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
|
||||
|
|
@@ -0,0 +1,8 @@
|
||||
info: Microsoft.Hosting.Lifetime[14]
|
||||
Now listening on: http://localhost:55417
|
||||
info: Microsoft.Hosting.Lifetime[0]
|
||||
Application started. Press Ctrl+C to shut down.
|
||||
info: Microsoft.Hosting.Lifetime[0]
|
||||
Hosting environment: Production
|
||||
info: Microsoft.Hosting.Lifetime[0]
|
||||
Content root path: C:\Users\koi\source\repos\Ai\TrafagSalesExporter
|
||||
@@ -0,0 +1,23 @@
|
||||
Sage SQL CSV export
|
||||
===================
|
||||
|
||||
Server instance: localhost
|
||||
Database filter: Sage
|
||||
From date: 2025-01-01
|
||||
To date: 2026-01-01
|
||||
|
||||
Files:
|
||||
- candidate_objects.csv: SQL tables/views that look relevant for sales/invoices.
|
||||
- export_summary.csv: export status and row counts.
|
||||
- *.csv: exported samples or selected full exports.
|
||||
|
||||
Recommended workflow:
|
||||
1. Run discovery first:
|
||||
.\Export-SageSqlCsv.ps1 -DiscoverOnly
|
||||
2. Send candidate_objects.csv to Trafag/IT for selection.
|
||||
3. Export selected objects:
|
||||
.\Export-SageSqlCsv.ps1 -Database "DATABASE_NAME" -ObjectName "schema.table_or_view"
|
||||
4. If the selected object is very large, add:
|
||||
-FromDate "2025-01-01" -ToDate "2026-01-01" -MaxRowsPerObject 100000
|
||||
|
||||
The script only reads data. It does not change SQL Server or Sage.
|
||||
+1974
File diff suppressed because it is too large
Load Diff
+16977
File diff suppressed because it is too large
Load Diff
+1
@@ -0,0 +1 @@
|
||||
"CodigoEmpresa";"EstadisClave1";"EstadisClave2";"EstadisClave3";"Ejercicio";"Periodo";"Origen";"CodigoZona";"CodigoJefeZona_";"CodigoJefeVenta_";"CodigoComisionista";"CodigoComisionista2_";"CodigoComisionista3_";"CodigoComisionista4_";"CodigoCliente";"CodigoFamilia";"CodigoSubfamilia";"CodigoArticulo";"CodigoColor_";"GrupoTalla_";"UnidadesTalla01_";"UnidadesTalla02_";"UnidadesTalla03_";"UnidadesTalla04_";"UnidadesTalla05_";"UnidadesTalla06_";"UnidadesTalla07_";"UnidadesTalla08_";"UnidadesTalla09_";"UnidadesTalla10_";"UnidadesTalla11_";"UnidadesTalla12_";"UnidadesTalla13_";"UnidadesTalla14_";"UnidadesTalla15_";"UnidadesTalla16_";"UnidadesTalla17_";"UnidadesTalla18_";"UnidadesTalla19_";"UnidadesTalla20_";"UnidadesTalla21_";"UnidadesTalla22_";"UnidadesTalla23_";"UnidadesTalla24_";"UnidadesTalla25_";"UnidadesTalla26_";"UnidadesTalla27_";"UnidadesTalla28_";"UnidadesTalla29_";"UnidadesTalla30_";"UnidadesTalla31_";"UnidadesTalla32_";"UnidadesTalla33_";"UnidadesTalla34_";"UnidadesTalla35_";"UnidadesTalla36_";"UnidadesTalla37_";"UnidadesTalla38_";"UnidadesTalla39_";"UnidadesTalla40_";"ImporteTalla01_";"ImporteTalla02_";"ImporteTalla03_";"ImporteTalla04_";"ImporteTalla05_";"ImporteTalla06_";"ImporteTalla07_";"ImporteTalla08_";"ImporteTalla09_";"ImporteTalla10_";"ImporteTalla11_";"ImporteTalla12_";"ImporteTalla13_";"ImporteTalla14_";"ImporteTalla15_";"ImporteTalla16_";"ImporteTalla17_";"ImporteTalla18_";"ImporteTalla19_";"ImporteTalla20_";"ImporteTalla21_";"ImporteTalla22_";"ImporteTalla23_";"ImporteTalla24_";"ImporteTalla25_";"ImporteTalla26_";"ImporteTalla27_";"ImporteTalla28_";"ImporteTalla29_";"ImporteTalla30_";"ImporteTalla31_";"ImporteTalla32_";"ImporteTalla33_";"ImporteTalla34_";"ImporteTalla35_";"ImporteTalla36_";"ImporteTalla37_";"ImporteTalla38_";"ImporteTalla39_";"ImporteTalla40_";"UnidadesTotalTallas_";"ImporteTotalTallas_"
|
||||
|
+3789
File diff suppressed because it is too large
Load Diff
+36412
File diff suppressed because it is too large
Load Diff
+6518
File diff suppressed because it is too large
Load Diff
+1
@@ -0,0 +1 @@
|
||||
"oppCoId";"effeForecast";"effeNumber";"invoExercise";"invoSeries";"invoNumber";"effeOrder";"statusDelete";"customerCode";"customer";"effeAmount";"expirationDate";"invoDate";"emissionDate";"accountCode";"counterPart";"comment";"canalCode";"statusRemitted";"remittedType";"remittedDate";"remittedBank";"remittedNumber";"statusRisk";"statusUnpaid";"salesPersonId";"salesPerson";"effectType";"effectClass";"effeId";"invoId"
|
||||
|
+81
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
||||
"Database";"Object";"Action";"Rows";"File";"DateColumn";"Error"
|
||||
"Sage";"dbo.CabeceraAlbaranCliente";"Exported";"1973";"C:\Users\Administrador\Desktop\Sage_SQL_CSV_Export_20260505_102507\Sage.dbo.CabeceraAlbaranCliente.csv";"FechaFactura";""
|
||||
"Sage";"dbo.LineasAlbaranCliente";"Exported";"4814";"C:\Users\Administrador\Desktop\Sage_SQL_CSV_Export_20260505_102507\Sage.dbo.LineasAlbaranCliente.csv";"FechaRegistro";""
|
||||
"Sage";"dbo.EstadisVenta";"Exported";"16976";"C:\Users\Administrador\Desktop\Sage_SQL_CSV_Export_20260505_102507\Sage.dbo.EstadisVenta.csv";;""
|
||||
"Sage";"dbo.EstadisVentaTallas";"Exported";"0";"C:\Users\Administrador\Desktop\Sage_SQL_CSV_Export_20260505_102507\Sage.dbo.EstadisVentaTallas.csv";;""
|
||||
"Sage";"dbo.FacturasTB";"Exported";"3788";"C:\Users\Administrador\Desktop\Sage_SQL_CSV_Export_20260505_102507\Sage.dbo.FacturasTB.csv";"FechaFactura";""
|
||||
"Sage";"dbo.MovimientosFacturas";"Exported";"6517";"C:\Users\Administrador\Desktop\Sage_SQL_CSV_Export_20260505_102507\Sage.dbo.MovimientosFacturas.csv";"FechaFactura";""
|
||||
"Sage";"dbo.Vis_RTDV_EfectosFactura";"Exported";"0";"C:\Users\Administrador\Desktop\Sage_SQL_CSV_Export_20260505_102507\Sage.dbo.Vis_RTDV_EfectosFactura.csv";"expirationDate";""
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
Sage Spain Sales CSV export
|
||||
===========================
|
||||
|
||||
Created: 2026-05-05 10:44:06
|
||||
Server instance: localhost
|
||||
Database: Sage
|
||||
From date: 2025-01-01
|
||||
To date: 2026-01-01
|
||||
|
||||
Output:
|
||||
C:\Users\Administrador\Desktop\Sage_Spain_Sales_Export_20260505_104358\Spain_Sales_2025.csv
|
||||
|
||||
Rows:
|
||||
4341
|
||||
|
||||
SalesPriceValue sum:
|
||||
3082320.180000
|
||||
|
||||
Source:
|
||||
dbo.CabeceraAlbaranCliente joined with dbo.LineasAlbaranCliente
|
||||
|
||||
Filter:
|
||||
CabeceraAlbaranCliente.FechaFactura >= FromDate
|
||||
CabeceraAlbaranCliente.FechaFactura < ToDate
|
||||
|
||||
Notes:
|
||||
- Currency is set to EUR because Sage exports EnEuros_=-1 and CodigoDivisa is empty in the analysed rows.
|
||||
- SalesPriceValue uses LineasAlbaranCliente.ImporteNeto.
|
||||
- DocumentNetAmount uses CabeceraAlbaranCliente.BaseImponible.
|
||||
- Credit notes are marked when TipoNuevaFra=2, SerieFactura='REC', or StatusAbono is non-zero.
|
||||
@@ -0,0 +1,16 @@
|
||||
$scriptPath = Join-Path $PSScriptRoot "Export-SageSqlCsv.ps1"
|
||||
|
||||
& $scriptPath `
|
||||
-Database "Sage" `
|
||||
-ObjectName @(
|
||||
"dbo.CabeceraAlbaranCliente",
|
||||
"dbo.LineasAlbaranCliente",
|
||||
"dbo.EstadisVenta",
|
||||
"dbo.EstadisVentaTallas",
|
||||
"dbo.FacturasTB",
|
||||
"dbo.MovimientosFacturas",
|
||||
"dbo.Vis_RTDV_EfectosFactura"
|
||||
) `
|
||||
-FromDate "2025-01-01" `
|
||||
-ToDate "2026-01-01" `
|
||||
-MaxRowsPerObject 10000
|
||||
@@ -0,0 +1,15 @@
|
||||
$scriptPath = Join-Path $PSScriptRoot "Export-SageSqlCsv.ps1"
|
||||
|
||||
& $scriptPath `
|
||||
-Database "Sage" `
|
||||
-ObjectName @(
|
||||
"dbo.CabeceraAlbaranCliente",
|
||||
"dbo.LineasAlbaranCliente",
|
||||
"dbo.EstadisVenta",
|
||||
"dbo.EstadisVentaTallas",
|
||||
"dbo.FacturasTB",
|
||||
"dbo.MovimientosFacturas",
|
||||
"dbo.Vis_RTDV_EfectosFactura"
|
||||
) `
|
||||
-FromDate "2025-01-01" `
|
||||
-ToDate "2026-01-01"
|
||||
@@ -0,0 +1,215 @@
|
||||
param(
|
||||
[string]$ServerInstance = "localhost",
|
||||
[string]$Database = "Sage",
|
||||
[datetime]$FromDate = "2025-01-01",
|
||||
[datetime]$ToDate = "2026-01-01",
|
||||
[string]$OutputDirectory = (Join-Path $env:USERPROFILE "Desktop")
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function New-Connection {
|
||||
$builder = New-Object System.Data.SqlClient.SqlConnectionStringBuilder
|
||||
$builder["Data Source"] = $ServerInstance
|
||||
$builder["Initial Catalog"] = $Database
|
||||
$builder["Integrated Security"] = $true
|
||||
$builder["TrustServerCertificate"] = $true
|
||||
$builder["Connect Timeout"] = 15
|
||||
return New-Object System.Data.SqlClient.SqlConnection($builder.ConnectionString)
|
||||
}
|
||||
|
||||
function Convert-ToCsvValue {
|
||||
param($Value)
|
||||
|
||||
if ($null -eq $Value -or $Value -is [System.DBNull]) {
|
||||
return ""
|
||||
}
|
||||
|
||||
if ($Value -is [datetime]) {
|
||||
$text = $Value.ToString("yyyy-MM-dd HH:mm:ss")
|
||||
}
|
||||
else {
|
||||
$text = [string]$Value
|
||||
}
|
||||
|
||||
$text = $text.Replace('"', '""')
|
||||
return '"' + $text + '"'
|
||||
}
|
||||
|
||||
function Export-QueryToCsv {
|
||||
param(
|
||||
[string]$Sql,
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
$conn = New-Connection
|
||||
$cmd = $conn.CreateCommand()
|
||||
$cmd.CommandText = $Sql
|
||||
$cmd.CommandTimeout = 0
|
||||
|
||||
$fromParameter = $cmd.Parameters.Add("@FromDate", [System.Data.SqlDbType]::Date)
|
||||
$fromParameter.Value = $FromDate.Date
|
||||
|
||||
$toParameter = $cmd.Parameters.Add("@ToDate", [System.Data.SqlDbType]::Date)
|
||||
$toParameter.Value = $ToDate.Date
|
||||
|
||||
$writer = New-Object System.IO.StreamWriter($Path, $false, [System.Text.Encoding]::UTF8)
|
||||
$rowCount = 0
|
||||
$salesSum = [decimal]0
|
||||
|
||||
try {
|
||||
$conn.Open()
|
||||
$reader = $cmd.ExecuteReader()
|
||||
|
||||
$headers = for ($i = 0; $i -lt $reader.FieldCount; $i++) {
|
||||
Convert-ToCsvValue $reader.GetName($i)
|
||||
}
|
||||
$writer.WriteLine(($headers -join ";"))
|
||||
|
||||
$salesIndex = -1
|
||||
for ($i = 0; $i -lt $reader.FieldCount; $i++) {
|
||||
if ($reader.GetName($i) -eq "SalesPriceValue") {
|
||||
$salesIndex = $i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
while ($reader.Read()) {
|
||||
$values = for ($i = 0; $i -lt $reader.FieldCount; $i++) {
|
||||
Convert-ToCsvValue $reader.GetValue($i)
|
||||
}
|
||||
$writer.WriteLine(($values -join ";"))
|
||||
$rowCount++
|
||||
|
||||
if ($salesIndex -ge 0 -and -not $reader.IsDBNull($salesIndex)) {
|
||||
$salesSum += [decimal]$reader.GetValue($salesIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
$writer.Dispose()
|
||||
$conn.Dispose()
|
||||
}
|
||||
|
||||
return [pscustomobject]@{
|
||||
Rows = $rowCount
|
||||
SalesPriceValueSum = $salesSum
|
||||
}
|
||||
}
|
||||
|
||||
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
||||
$runDirectory = Join-Path $OutputDirectory "Sage_Spain_Sales_Export_$timestamp"
|
||||
New-Item -ItemType Directory -Path $runDirectory -Force | Out-Null
|
||||
|
||||
$csvPath = Join-Path $runDirectory "Spain_Sales_2025.csv"
|
||||
$summaryPath = Join-Path $runDirectory "Spain_Sales_2025_summary.txt"
|
||||
|
||||
$sql = @"
|
||||
SELECT
|
||||
'TRES' AS TSC,
|
||||
'Spanien' AS Land,
|
||||
'Sage' AS SourceSystem,
|
||||
c.CodigoEmpresa AS CompanyCode,
|
||||
c.EjercicioAlbaran AS DeliveryYear,
|
||||
c.SerieAlbaran AS DeliverySeries,
|
||||
c.NumeroAlbaran AS DeliveryNumber,
|
||||
c.EjercicioFactura AS InvoiceYear,
|
||||
c.SerieFactura AS InvoiceSeries,
|
||||
c.NumeroFactura AS InvoiceNumber,
|
||||
l.Orden AS PositionOnInvoice,
|
||||
l.LineasPosicion AS SourceLineId,
|
||||
l.CodigoArticulo AS Material,
|
||||
l.DescripcionArticulo AS Name,
|
||||
l.Descripcion2Articulo AS Description2,
|
||||
l.DescripcionLinea AS DescriptionLine,
|
||||
l.CodigoFamilia AS ProductGroup,
|
||||
l.CodigoSubfamilia AS ProductSubGroup,
|
||||
CAST(l.Unidades AS decimal(19, 6)) AS Quantity,
|
||||
c.CodigoCliente AS CustomerNumber,
|
||||
c.Nombre AS CustomerName,
|
||||
c.CodigoNacion AS CustomerCountryCode,
|
||||
c.Nacion AS CustomerCountry,
|
||||
CAST(l.PrecioCoste AS decimal(19, 6)) AS StandardCost,
|
||||
CAST(l.ImporteCoste AS decimal(19, 6)) AS StandardCostValue,
|
||||
'EUR' AS StandardCostCurrency,
|
||||
CAST(l.ImporteNeto AS decimal(19, 6)) AS SalesPriceValue,
|
||||
'EUR' AS SalesCurrency,
|
||||
'EUR' AS DocumentCurrency,
|
||||
'EUR' AS CompanyCurrency,
|
||||
c.CodigoDivisa AS SageCurrencyCode,
|
||||
CAST(c.BaseImponible AS decimal(19, 6)) AS DocumentNetAmount,
|
||||
CAST(c.TotalIva AS decimal(19, 6)) AS DocumentVatAmount,
|
||||
CAST(c.ImporteFactura AS decimal(19, 6)) AS DocumentGrossAmount,
|
||||
c.FechaFactura AS InvoiceDate,
|
||||
c.FechaAlbaran AS DeliveryDate,
|
||||
l.FechaRegistro AS LineRegistrationDate,
|
||||
c.EjercicioPedido AS OrderYear,
|
||||
c.SeriePedido AS OrderSeries,
|
||||
c.NumeroPedido AS OrderNumber,
|
||||
c.SuPedido AS PurchaseOrderNumber,
|
||||
c.CodigoExportacion_ AS Incoterms2020,
|
||||
c.CondicionExportacion_ AS IncotermsText,
|
||||
c.CodigoComisionista AS SalesResponsibleEmployee,
|
||||
c.StatusAbono AS CreditStatus,
|
||||
c.NoFacturable AS NonBillable,
|
||||
c.TipoNuevaFra AS InvoiceType,
|
||||
c.StatusFacturado AS BillingStatus,
|
||||
CASE
|
||||
WHEN c.TipoNuevaFra = 2 OR c.SerieFactura = 'REC' OR c.StatusAbono <> 0 THEN 'Credit Note'
|
||||
ELSE 'Invoice'
|
||||
END AS DocumentType
|
||||
FROM dbo.CabeceraAlbaranCliente c
|
||||
JOIN dbo.LineasAlbaranCliente l
|
||||
ON l.CodigoEmpresa = c.CodigoEmpresa
|
||||
AND l.EjercicioAlbaran = c.EjercicioAlbaran
|
||||
AND l.SerieAlbaran = c.SerieAlbaran
|
||||
AND l.NumeroAlbaran = c.NumeroAlbaran
|
||||
WHERE c.FechaFactura >= @FromDate
|
||||
AND c.FechaFactura < @ToDate
|
||||
ORDER BY
|
||||
c.FechaFactura,
|
||||
c.SerieFactura,
|
||||
c.NumeroFactura,
|
||||
l.Orden;
|
||||
"@
|
||||
|
||||
$result = Export-QueryToCsv -Sql $sql -Path $csvPath
|
||||
|
||||
@"
|
||||
Sage Spain Sales CSV export
|
||||
===========================
|
||||
|
||||
Created: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
|
||||
Server instance: $ServerInstance
|
||||
Database: $Database
|
||||
From date: $($FromDate.ToString("yyyy-MM-dd"))
|
||||
To date: $($ToDate.ToString("yyyy-MM-dd"))
|
||||
|
||||
Output:
|
||||
$csvPath
|
||||
|
||||
Rows:
|
||||
$($result.Rows)
|
||||
|
||||
SalesPriceValue sum:
|
||||
$($result.SalesPriceValueSum)
|
||||
|
||||
Source:
|
||||
dbo.CabeceraAlbaranCliente joined with dbo.LineasAlbaranCliente
|
||||
|
||||
Filter:
|
||||
CabeceraAlbaranCliente.FechaFactura >= FromDate
|
||||
CabeceraAlbaranCliente.FechaFactura < ToDate
|
||||
|
||||
Notes:
|
||||
- Currency is set to EUR because Sage exports EnEuros_=-1 and CodigoDivisa is empty in the analysed rows.
|
||||
- SalesPriceValue uses LineasAlbaranCliente.ImporteNeto.
|
||||
- DocumentNetAmount uses CabeceraAlbaranCliente.BaseImponible.
|
||||
- Credit notes are marked when TipoNuevaFra=2, SerieFactura='REC', or StatusAbono is non-zero.
|
||||
"@ | Set-Content -LiteralPath $summaryPath -Encoding UTF8
|
||||
|
||||
Write-Host "Created:"
|
||||
Write-Host " $csvPath"
|
||||
Write-Host " $summaryPath"
|
||||
Write-Host "Rows: $($result.Rows)"
|
||||
Write-Host "SalesPriceValue sum: $($result.SalesPriceValueSum)"
|
||||
@@ -0,0 +1,410 @@
|
||||
param(
|
||||
[string]$ServerInstance = "localhost",
|
||||
[string]$Database = "",
|
||||
[string[]]$ObjectName = @(),
|
||||
[datetime]$FromDate = "2025-01-01",
|
||||
[datetime]$ToDate = "2026-01-01",
|
||||
[string]$OutputDirectory = (Join-Path $env:USERPROFILE "Desktop"),
|
||||
[int]$SampleRows = 500,
|
||||
[int]$MaxRowsPerObject = 0,
|
||||
[switch]$DiscoverOnly,
|
||||
[switch]$ExportCandidates,
|
||||
[switch]$IncludeSystemDatabases
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function New-Connection {
|
||||
param([string]$DbName)
|
||||
|
||||
$builder = New-Object System.Data.SqlClient.SqlConnectionStringBuilder
|
||||
$builder["Data Source"] = $ServerInstance
|
||||
$builder["Initial Catalog"] = $DbName
|
||||
$builder["Integrated Security"] = $true
|
||||
$builder["TrustServerCertificate"] = $true
|
||||
$builder["Connect Timeout"] = 15
|
||||
return New-Object System.Data.SqlClient.SqlConnection($builder.ConnectionString)
|
||||
}
|
||||
|
||||
function Invoke-DataTable {
|
||||
param(
|
||||
[string]$DbName,
|
||||
[string]$Sql,
|
||||
[hashtable]$Parameters = @{}
|
||||
)
|
||||
|
||||
$conn = New-Connection $DbName
|
||||
$cmd = $conn.CreateCommand()
|
||||
$cmd.CommandText = $Sql
|
||||
$cmd.CommandTimeout = 300
|
||||
|
||||
foreach ($key in $Parameters.Keys) {
|
||||
$param = $cmd.Parameters.Add("@$key", [System.Data.SqlDbType]::NVarChar, 4000)
|
||||
$param.Value = [string]$Parameters[$key]
|
||||
}
|
||||
|
||||
$table = New-Object System.Data.DataTable
|
||||
try {
|
||||
$conn.Open()
|
||||
$reader = $cmd.ExecuteReader()
|
||||
$table.Load($reader)
|
||||
}
|
||||
finally {
|
||||
$conn.Dispose()
|
||||
}
|
||||
|
||||
return $table
|
||||
}
|
||||
|
||||
function Convert-ToCsvValue {
|
||||
param($Value)
|
||||
|
||||
if ($null -eq $Value -or $Value -is [System.DBNull]) {
|
||||
return ""
|
||||
}
|
||||
|
||||
if ($Value -is [datetime]) {
|
||||
$text = $Value.ToString("yyyy-MM-dd HH:mm:ss")
|
||||
}
|
||||
else {
|
||||
$text = [string]$Value
|
||||
}
|
||||
|
||||
$text = $text.Replace('"', '""')
|
||||
return '"' + $text + '"'
|
||||
}
|
||||
|
||||
function Export-QueryToCsv {
|
||||
param(
|
||||
[string]$DbName,
|
||||
[string]$Sql,
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
$conn = New-Connection $DbName
|
||||
$cmd = $conn.CreateCommand()
|
||||
$cmd.CommandText = $Sql
|
||||
$cmd.CommandTimeout = 0
|
||||
|
||||
$writer = New-Object System.IO.StreamWriter($Path, $false, [System.Text.Encoding]::UTF8)
|
||||
$rowCount = 0
|
||||
|
||||
try {
|
||||
$conn.Open()
|
||||
$reader = $cmd.ExecuteReader()
|
||||
|
||||
$headers = for ($i = 0; $i -lt $reader.FieldCount; $i++) {
|
||||
Convert-ToCsvValue $reader.GetName($i)
|
||||
}
|
||||
$writer.WriteLine(($headers -join ";"))
|
||||
|
||||
while ($reader.Read()) {
|
||||
$values = for ($i = 0; $i -lt $reader.FieldCount; $i++) {
|
||||
Convert-ToCsvValue $reader.GetValue($i)
|
||||
}
|
||||
$writer.WriteLine(($values -join ";"))
|
||||
$rowCount++
|
||||
}
|
||||
}
|
||||
finally {
|
||||
$writer.Dispose()
|
||||
$conn.Dispose()
|
||||
}
|
||||
|
||||
return $rowCount
|
||||
}
|
||||
|
||||
function Quote-NamePart {
|
||||
param([string]$Name)
|
||||
|
||||
return "[" + $Name.Replace("]", "]]") + "]"
|
||||
}
|
||||
|
||||
function Split-SqlObjectName {
|
||||
param([string]$Name)
|
||||
|
||||
$parts = $Name.Split(".", 2)
|
||||
if ($parts.Count -eq 1) {
|
||||
return [pscustomobject]@{ SchemaName = "dbo"; ObjectName = $parts[0] }
|
||||
}
|
||||
|
||||
return [pscustomobject]@{ SchemaName = $parts[0].Trim("[", "]"); ObjectName = $parts[1].Trim("[", "]") }
|
||||
}
|
||||
|
||||
function Get-UserDatabases {
|
||||
$sql = @"
|
||||
SELECT name
|
||||
FROM sys.databases
|
||||
WHERE state_desc = 'ONLINE'
|
||||
AND HAS_DBACCESS(name) = 1
|
||||
$(if ($IncludeSystemDatabases) { "" } else { "AND database_id > 4" })
|
||||
ORDER BY name;
|
||||
"@
|
||||
|
||||
Invoke-DataTable "master" $sql | ForEach-Object { $_.name }
|
||||
}
|
||||
|
||||
function Get-CandidateObjects {
|
||||
param([string]$DbName)
|
||||
|
||||
$sql = @"
|
||||
WITH object_columns AS (
|
||||
SELECT
|
||||
s.name AS SchemaName,
|
||||
o.name AS ObjectName,
|
||||
o.type_desc AS ObjectType,
|
||||
c.name AS ColumnName,
|
||||
t.name AS TypeName,
|
||||
c.max_length,
|
||||
c.precision,
|
||||
c.scale
|
||||
FROM sys.objects o
|
||||
JOIN sys.schemas s ON s.schema_id = o.schema_id
|
||||
JOIN sys.columns c ON c.object_id = o.object_id
|
||||
JOIN sys.types t ON t.user_type_id = c.user_type_id
|
||||
WHERE o.type IN ('U', 'V')
|
||||
AND o.is_ms_shipped = 0
|
||||
),
|
||||
scored AS (
|
||||
SELECT
|
||||
SchemaName,
|
||||
ObjectName,
|
||||
ObjectType,
|
||||
SUM(CASE WHEN LOWER(ObjectName) LIKE '%fact%' OR LOWER(ObjectName) LIKE '%invoice%' OR LOWER(ObjectName) LIKE '%venta%' OR LOWER(ObjectName) LIKE '%sales%' OR LOWER(ObjectName) LIKE '%albar%' OR LOWER(ObjectName) LIKE '%pedido%' THEN 5 ELSE 0 END) +
|
||||
SUM(CASE WHEN LOWER(ColumnName) LIKE '%fecha%' OR LOWER(ColumnName) LIKE '%date%' THEN 2 ELSE 0 END) +
|
||||
SUM(CASE WHEN LOWER(ColumnName) LIKE '%cliente%' OR LOWER(ColumnName) LIKE '%customer%' THEN 2 ELSE 0 END) +
|
||||
SUM(CASE WHEN LOWER(ColumnName) LIKE '%articulo%' OR LOWER(ColumnName) LIKE '%item%' OR LOWER(ColumnName) LIKE '%producto%' THEN 2 ELSE 0 END) +
|
||||
SUM(CASE WHEN LOWER(ColumnName) LIKE '%importe%' OR LOWER(ColumnName) LIKE '%neto%' OR LOWER(ColumnName) LIKE '%total%' OR LOWER(ColumnName) LIKE '%amount%' THEN 3 ELSE 0 END) +
|
||||
SUM(CASE WHEN LOWER(ColumnName) LIKE '%cantidad%' OR LOWER(ColumnName) LIKE '%quantity%' OR LOWER(ColumnName) LIKE '%unidades%' THEN 2 ELSE 0 END) AS Score,
|
||||
COUNT(*) AS ColumnCount,
|
||||
STRING_AGG(CONVERT(nvarchar(max), ColumnName), ', ') WITHIN GROUP (ORDER BY ColumnName) AS Columns
|
||||
FROM object_columns
|
||||
GROUP BY SchemaName, ObjectName, ObjectType
|
||||
)
|
||||
SELECT TOP (80)
|
||||
DB_NAME() AS DatabaseName,
|
||||
SchemaName,
|
||||
ObjectName,
|
||||
ObjectType,
|
||||
Score,
|
||||
ColumnCount,
|
||||
Columns
|
||||
FROM scored
|
||||
WHERE Score > 0
|
||||
ORDER BY Score DESC, ObjectName;
|
||||
"@
|
||||
|
||||
Invoke-DataTable $DbName $sql
|
||||
}
|
||||
|
||||
function Get-DateColumns {
|
||||
param(
|
||||
[string]$DbName,
|
||||
[string]$SchemaName,
|
||||
[string]$ObjectNameValue
|
||||
)
|
||||
|
||||
$sql = @"
|
||||
SELECT c.name AS ColumnName
|
||||
FROM sys.objects o
|
||||
JOIN sys.schemas s ON s.schema_id = o.schema_id
|
||||
JOIN sys.columns c ON c.object_id = o.object_id
|
||||
JOIN sys.types t ON t.user_type_id = c.user_type_id
|
||||
WHERE s.name = @schema
|
||||
AND o.name = @object
|
||||
AND (
|
||||
t.name IN ('date', 'datetime', 'datetime2', 'smalldatetime')
|
||||
OR LOWER(c.name) LIKE '%fecha%'
|
||||
OR LOWER(c.name) LIKE '%date%'
|
||||
)
|
||||
ORDER BY
|
||||
CASE
|
||||
WHEN LOWER(c.name) LIKE '%fact%' OR LOWER(c.name) LIKE '%invoice%' THEN 0
|
||||
WHEN LOWER(c.name) LIKE '%fecha%' OR LOWER(c.name) LIKE '%date%' THEN 1
|
||||
ELSE 2
|
||||
END,
|
||||
c.column_id;
|
||||
"@
|
||||
|
||||
Invoke-DataTable $DbName $sql @{ schema = $SchemaName; object = $ObjectNameValue } |
|
||||
ForEach-Object { $_.ColumnName }
|
||||
}
|
||||
|
||||
function Build-SelectSql {
|
||||
param(
|
||||
[string]$SchemaName,
|
||||
[string]$ObjectNameValue,
|
||||
[string]$DateColumn,
|
||||
[int]$TopRows
|
||||
)
|
||||
|
||||
$topClause = if ($TopRows -gt 0) { "TOP ($TopRows)" } else { "" }
|
||||
$qualified = "$(Quote-NamePart $SchemaName).$(Quote-NamePart $ObjectNameValue)"
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($DateColumn)) {
|
||||
return "SELECT $topClause * FROM $qualified;"
|
||||
}
|
||||
|
||||
$from = $FromDate.ToString("yyyy-MM-dd")
|
||||
$to = $ToDate.ToString("yyyy-MM-dd")
|
||||
$dateColumnSql = Quote-NamePart $DateColumn
|
||||
|
||||
return @"
|
||||
SELECT $topClause *
|
||||
FROM $qualified
|
||||
WHERE TRY_CONVERT(date, $dateColumnSql) >= CONVERT(date, '$from')
|
||||
AND TRY_CONVERT(date, $dateColumnSql) < CONVERT(date, '$to')
|
||||
ORDER BY TRY_CONVERT(date, $dateColumnSql);
|
||||
"@
|
||||
}
|
||||
|
||||
function Normalize-FileName {
|
||||
param([string]$Value)
|
||||
|
||||
return ($Value -replace '[\\/:*?"<>|]', '_')
|
||||
}
|
||||
|
||||
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
||||
$runDirectory = Join-Path $OutputDirectory "Sage_SQL_CSV_Export_$timestamp"
|
||||
New-Item -ItemType Directory -Path $runDirectory -Force | Out-Null
|
||||
|
||||
$databases = if ([string]::IsNullOrWhiteSpace($Database)) {
|
||||
@(Get-UserDatabases)
|
||||
}
|
||||
else {
|
||||
@($Database)
|
||||
}
|
||||
|
||||
$summary = New-Object System.Collections.Generic.List[object]
|
||||
$allCandidates = New-Object System.Collections.Generic.List[object]
|
||||
|
||||
foreach ($db in $databases) {
|
||||
Write-Host "Scanning database: $db"
|
||||
try {
|
||||
$candidates = @(Get-CandidateObjects $db)
|
||||
foreach ($candidate in $candidates) {
|
||||
$allCandidates.Add($candidate)
|
||||
}
|
||||
}
|
||||
catch {
|
||||
$summary.Add([pscustomobject]@{
|
||||
Database = $db
|
||||
Object = ""
|
||||
Action = "Discovery failed"
|
||||
Rows = 0
|
||||
File = ""
|
||||
Error = $_.Exception.Message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
$candidatePath = Join-Path $runDirectory "candidate_objects.csv"
|
||||
if ($allCandidates.Count -gt 0) {
|
||||
$allCandidates | Export-Csv -LiteralPath $candidatePath -NoTypeInformation -Encoding UTF8 -Delimiter ";"
|
||||
}
|
||||
|
||||
if (-not $DiscoverOnly) {
|
||||
$objectsToExport = New-Object System.Collections.Generic.List[object]
|
||||
|
||||
foreach ($name in $ObjectName) {
|
||||
if ([string]::IsNullOrWhiteSpace($name)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Database)) {
|
||||
throw "When -ObjectName is used, pass -Database as well."
|
||||
}
|
||||
|
||||
$parsed = Split-SqlObjectName $name
|
||||
$objectsToExport.Add([pscustomobject]@{
|
||||
DatabaseName = $Database
|
||||
SchemaName = $parsed.SchemaName
|
||||
ObjectName = $parsed.ObjectName
|
||||
})
|
||||
}
|
||||
|
||||
if ($ExportCandidates) {
|
||||
foreach ($candidate in ($allCandidates | Sort-Object DatabaseName, @{Expression="Score"; Descending=$true} | Select-Object -First 25)) {
|
||||
$objectsToExport.Add([pscustomobject]@{
|
||||
DatabaseName = $candidate.DatabaseName
|
||||
SchemaName = $candidate.SchemaName
|
||||
ObjectName = $candidate.ObjectName
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($object in $objectsToExport) {
|
||||
$db = $object.DatabaseName
|
||||
$schema = $object.SchemaName
|
||||
$objectNameValue = $object.ObjectName
|
||||
|
||||
try {
|
||||
$dateColumn = @(Get-DateColumns $db $schema $objectNameValue | Select-Object -First 1)[0]
|
||||
$limit = if ($MaxRowsPerObject -gt 0) { $MaxRowsPerObject } elseif ($ObjectName.Count -gt 0) { 0 } else { $SampleRows }
|
||||
$sql = Build-SelectSql $schema $objectNameValue $dateColumn $limit
|
||||
$fileName = Normalize-FileName "$db.$schema.$objectNameValue.csv"
|
||||
$path = Join-Path $runDirectory $fileName
|
||||
Write-Host "Exporting $db.$schema.$objectNameValue -> $path"
|
||||
$rows = Export-QueryToCsv $db $sql $path
|
||||
|
||||
$summary.Add([pscustomobject]@{
|
||||
Database = $db
|
||||
Object = "$schema.$objectNameValue"
|
||||
Action = "Exported"
|
||||
Rows = $rows
|
||||
File = $path
|
||||
DateColumn = $dateColumn
|
||||
Error = ""
|
||||
})
|
||||
}
|
||||
catch {
|
||||
$summary.Add([pscustomobject]@{
|
||||
Database = $db
|
||||
Object = "$schema.$objectNameValue"
|
||||
Action = "Export failed"
|
||||
Rows = 0
|
||||
File = ""
|
||||
DateColumn = ""
|
||||
Error = $_.Exception.Message
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$summaryPath = Join-Path $runDirectory "export_summary.csv"
|
||||
$summary | Export-Csv -LiteralPath $summaryPath -NoTypeInformation -Encoding UTF8 -Delimiter ";"
|
||||
|
||||
$readmePath = Join-Path $runDirectory "README.txt"
|
||||
@"
|
||||
Sage SQL CSV export
|
||||
===================
|
||||
|
||||
Server instance: $ServerInstance
|
||||
Database filter: $(if ($Database) { $Database } else { "(all accessible user databases)" })
|
||||
From date: $($FromDate.ToString("yyyy-MM-dd"))
|
||||
To date: $($ToDate.ToString("yyyy-MM-dd"))
|
||||
|
||||
Files:
|
||||
- candidate_objects.csv: SQL tables/views that look relevant for sales/invoices.
|
||||
- export_summary.csv: export status and row counts.
|
||||
- *.csv: exported samples or selected full exports.
|
||||
|
||||
Recommended workflow:
|
||||
1. Run discovery first:
|
||||
.\Export-SageSqlCsv.ps1 -DiscoverOnly
|
||||
2. Send candidate_objects.csv to Trafag/IT for selection.
|
||||
3. Export selected objects:
|
||||
.\Export-SageSqlCsv.ps1 -Database "DATABASE_NAME" -ObjectName "schema.table_or_view"
|
||||
4. If the selected object is very large, add:
|
||||
-FromDate "2025-01-01" -ToDate "2026-01-01" -MaxRowsPerObject 100000
|
||||
|
||||
The script only reads data. It does not change SQL Server or Sage.
|
||||
"@ | Set-Content -LiteralPath $readmePath -Encoding UTF8
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Created folder:"
|
||||
Write-Host " $runDirectory"
|
||||
Write-Host ""
|
||||
Write-Host "Main files:"
|
||||
Write-Host " $candidatePath"
|
||||
Write-Host " $summaryPath"
|
||||
@@ -0,0 +1,334 @@
|
||||
param(
|
||||
[string[]]$SqlInstance = @(),
|
||||
[string]$OutputDirectory = (Join-Path $env:USERPROFILE "Desktop"),
|
||||
[switch]$ScanProgramFiles
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Continue"
|
||||
|
||||
function Add-Section {
|
||||
param([string]$Title)
|
||||
|
||||
$script:reportLines += ""
|
||||
$script:reportLines += "============================================================"
|
||||
$script:reportLines += $Title
|
||||
$script:reportLines += "============================================================"
|
||||
}
|
||||
|
||||
function Add-Line {
|
||||
param([string]$Text = "")
|
||||
|
||||
$script:reportLines += $Text
|
||||
}
|
||||
|
||||
function Read-RegistryValues {
|
||||
param([string]$Path)
|
||||
|
||||
try {
|
||||
if (Test-Path $Path) {
|
||||
return Get-ItemProperty -Path $Path -ErrorAction Stop
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Add-Line "Registry read failed: $Path :: $($_.Exception.Message)"
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
function Get-SageUninstallEntries {
|
||||
$paths = @(
|
||||
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
|
||||
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*",
|
||||
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"
|
||||
)
|
||||
|
||||
foreach ($path in $paths) {
|
||||
Get-ItemProperty -Path $path -ErrorAction SilentlyContinue |
|
||||
Where-Object {
|
||||
$_.DisplayName -and (
|
||||
$_.DisplayName -match "Sage" -or
|
||||
$_.Publisher -match "Sage"
|
||||
)
|
||||
} |
|
||||
Select-Object DisplayName, DisplayVersion, Publisher, InstallDate, InstallLocation, UninstallString, PSPath
|
||||
}
|
||||
}
|
||||
|
||||
function Get-SageFileVersions {
|
||||
$roots = @(
|
||||
$env:ProgramFiles,
|
||||
${env:ProgramFiles(x86)},
|
||||
$env:ProgramData
|
||||
) | Where-Object { $_ -and (Test-Path $_) }
|
||||
|
||||
foreach ($root in $roots) {
|
||||
Get-ChildItem -LiteralPath $root -Directory -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Name -match "Sage" } |
|
||||
ForEach-Object {
|
||||
Get-ChildItem -LiteralPath $_.FullName -Recurse -File -Include *.exe,*.dll -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.VersionInfo.ProductName -match "Sage" -or $_.VersionInfo.CompanyName -match "Sage" -or $_.Name -match "Sage" } |
|
||||
Select-Object FullName,
|
||||
@{Name="FileVersion"; Expression={$_.VersionInfo.FileVersion}},
|
||||
@{Name="ProductVersion"; Expression={$_.VersionInfo.ProductVersion}},
|
||||
@{Name="ProductName"; Expression={$_.VersionInfo.ProductName}},
|
||||
@{Name="CompanyName"; Expression={$_.VersionInfo.CompanyName}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Get-SqlInstancesFromRegistry {
|
||||
$instanceRoots = @(
|
||||
"HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL",
|
||||
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Microsoft SQL Server\Instance Names\SQL"
|
||||
)
|
||||
|
||||
$instances = New-Object System.Collections.Generic.List[object]
|
||||
|
||||
foreach ($root in $instanceRoots) {
|
||||
$values = Read-RegistryValues $root
|
||||
if (-not $values) {
|
||||
continue
|
||||
}
|
||||
|
||||
$values.PSObject.Properties |
|
||||
Where-Object { $_.Name -notlike "PS*" } |
|
||||
ForEach-Object {
|
||||
$instanceName = $_.Name
|
||||
$instanceId = [string]$_.Value
|
||||
$setupPaths = @(
|
||||
"HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$instanceId\Setup",
|
||||
"HKLM:\SOFTWARE\WOW6432Node\Microsoft\Microsoft SQL Server\$instanceId\Setup"
|
||||
)
|
||||
|
||||
foreach ($setupPath in $setupPaths) {
|
||||
$setup = Read-RegistryValues $setupPath
|
||||
if ($setup) {
|
||||
$instances.Add([pscustomobject]@{
|
||||
InstanceName = $instanceName
|
||||
InstanceId = $instanceId
|
||||
Edition = $setup.Edition
|
||||
Version = $setup.Version
|
||||
PatchLevel = $setup.PatchLevel
|
||||
ProductCode = $setup.ProductCode
|
||||
SQLPath = $setup.SQLPath
|
||||
SetupPath = $setupPath
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $instances
|
||||
}
|
||||
|
||||
function Get-SqlServices {
|
||||
Get-CimInstance Win32_Service -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Name -like "MSSQL*" -or $_.Name -like "SQLAgent*" -or $_.DisplayName -like "*SQL Server*" } |
|
||||
Select-Object Name, DisplayName, State, StartMode, PathName
|
||||
}
|
||||
|
||||
function Resolve-Sqlcmd {
|
||||
$cmd = Get-Command sqlcmd.exe -ErrorAction SilentlyContinue
|
||||
if ($cmd) {
|
||||
return $cmd.Source
|
||||
}
|
||||
|
||||
$candidates = @(
|
||||
"$env:ProgramFiles\Microsoft SQL Server\Client SDK\ODBC\*\Tools\Binn\sqlcmd.exe",
|
||||
"${env:ProgramFiles(x86)}\Microsoft SQL Server\Client SDK\ODBC\*\Tools\Binn\sqlcmd.exe",
|
||||
"$env:ProgramFiles\Microsoft SQL Server\*\Tools\Binn\sqlcmd.exe",
|
||||
"${env:ProgramFiles(x86)}\Microsoft SQL Server\*\Tools\Binn\sqlcmd.exe"
|
||||
)
|
||||
|
||||
foreach ($candidate in $candidates) {
|
||||
$match = Get-ChildItem -Path $candidate -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
if ($match) {
|
||||
return $match.FullName
|
||||
}
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
function Invoke-SqlVersionQuery {
|
||||
param(
|
||||
[string]$SqlcmdPath,
|
||||
[string]$Instance
|
||||
)
|
||||
|
||||
$query = @"
|
||||
SET NOCOUNT ON;
|
||||
SELECT
|
||||
@@VERSION AS FullVersion,
|
||||
SERVERPROPERTY('ProductVersion') AS ProductVersion,
|
||||
SERVERPROPERTY('ProductLevel') AS ProductLevel,
|
||||
SERVERPROPERTY('Edition') AS Edition,
|
||||
SERVERPROPERTY('EngineEdition') AS EngineEdition,
|
||||
SERVERPROPERTY('MachineName') AS MachineName,
|
||||
SERVERPROPERTY('ServerName') AS ServerName,
|
||||
SERVERPROPERTY('InstanceName') AS InstanceName,
|
||||
SERVERPROPERTY('Collation') AS Collation;
|
||||
"@
|
||||
|
||||
$tempQuery = Join-Path $env:TEMP ("sql_version_query_{0}.sql" -f ([guid]::NewGuid().ToString("N")))
|
||||
Set-Content -LiteralPath $tempQuery -Value $query -Encoding UTF8
|
||||
|
||||
try {
|
||||
$output = & $SqlcmdPath -S $Instance -E -i $tempQuery -W -s "|" -b 2>&1
|
||||
[pscustomobject]@{
|
||||
Instance = $Instance
|
||||
Success = $LASTEXITCODE -eq 0
|
||||
Output = ($output -join [Environment]::NewLine)
|
||||
}
|
||||
}
|
||||
catch {
|
||||
[pscustomobject]@{
|
||||
Instance = $Instance
|
||||
Success = $false
|
||||
Output = $_.Exception.Message
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Remove-Item -LiteralPath $tempQuery -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
||||
New-Item -ItemType Directory -Path $OutputDirectory -Force | Out-Null
|
||||
|
||||
$reportPath = Join-Path $OutputDirectory "Sage_SQL_Environment_$timestamp.txt"
|
||||
$jsonPath = Join-Path $OutputDirectory "Sage_SQL_Environment_$timestamp.json"
|
||||
$reportLines = New-Object System.Collections.Generic.List[string]
|
||||
|
||||
$computer = Get-CimInstance Win32_ComputerSystem -ErrorAction SilentlyContinue
|
||||
$os = Get-CimInstance Win32_OperatingSystem -ErrorAction SilentlyContinue
|
||||
$sageEntries = @(Get-SageUninstallEntries)
|
||||
$sqlRegistryInstances = @(Get-SqlInstancesFromRegistry)
|
||||
$sqlServices = @(Get-SqlServices)
|
||||
$sageFileVersions = @()
|
||||
|
||||
if ($ScanProgramFiles) {
|
||||
$sageFileVersions = @(Get-SageFileVersions)
|
||||
}
|
||||
|
||||
$sqlcmdPath = Resolve-Sqlcmd
|
||||
$queryInstances = New-Object System.Collections.Generic.List[string]
|
||||
|
||||
foreach ($instance in $SqlInstance) {
|
||||
if (-not [string]::IsNullOrWhiteSpace($instance)) {
|
||||
$queryInstances.Add($instance)
|
||||
}
|
||||
}
|
||||
|
||||
if ($queryInstances.Count -eq 0) {
|
||||
$machineName = $env:COMPUTERNAME
|
||||
foreach ($instance in $sqlRegistryInstances) {
|
||||
if ($instance.InstanceName -eq "MSSQLSERVER") {
|
||||
$queryInstances.Add("localhost")
|
||||
}
|
||||
else {
|
||||
$queryInstances.Add("localhost\$($instance.InstanceName)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$queryInstances = @($queryInstances | Select-Object -Unique)
|
||||
$sqlQueryResults = @()
|
||||
if ($sqlcmdPath -and $queryInstances.Count -gt 0) {
|
||||
foreach ($instance in $queryInstances) {
|
||||
$sqlQueryResults += Invoke-SqlVersionQuery -SqlcmdPath $sqlcmdPath -Instance $instance
|
||||
}
|
||||
}
|
||||
|
||||
Add-Section "Capture metadata"
|
||||
Add-Line "Timestamp: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")"
|
||||
Add-Line "Computer: $env:COMPUTERNAME"
|
||||
Add-Line "User: $env:USERDOMAIN\$env:USERNAME"
|
||||
Add-Line "Output text: $reportPath"
|
||||
Add-Line "Output json: $jsonPath"
|
||||
|
||||
Add-Section "Windows / machine"
|
||||
Add-Line "Manufacturer: $($computer.Manufacturer)"
|
||||
Add-Line "Model: $($computer.Model)"
|
||||
Add-Line "OS: $($os.Caption)"
|
||||
Add-Line "OS Version: $($os.Version)"
|
||||
Add-Line "OS Build: $($os.BuildNumber)"
|
||||
Add-Line "Install date: $($os.InstallDate)"
|
||||
|
||||
Add-Section "Sage entries from installed programs"
|
||||
if ($sageEntries.Count -eq 0) {
|
||||
Add-Line "No Sage uninstall entries found."
|
||||
}
|
||||
else {
|
||||
$sageEntries | Format-List | Out-String | ForEach-Object { Add-Line $_.TrimEnd() }
|
||||
}
|
||||
|
||||
Add-Section "Sage file versions"
|
||||
if (-not $ScanProgramFiles) {
|
||||
Add-Line "Skipped. Re-run with -ScanProgramFiles for file version scan."
|
||||
}
|
||||
elseif ($sageFileVersions.Count -eq 0) {
|
||||
Add-Line "No Sage file versions found under Program Files / ProgramData."
|
||||
}
|
||||
else {
|
||||
$sageFileVersions | Sort-Object ProductName, ProductVersion, FullName | Format-Table -AutoSize | Out-String | ForEach-Object { Add-Line $_.TrimEnd() }
|
||||
}
|
||||
|
||||
Add-Section "SQL Server instances from registry"
|
||||
if ($sqlRegistryInstances.Count -eq 0) {
|
||||
Add-Line "No SQL Server registry instances found."
|
||||
}
|
||||
else {
|
||||
$sqlRegistryInstances | Format-List | Out-String | ForEach-Object { Add-Line $_.TrimEnd() }
|
||||
}
|
||||
|
||||
Add-Section "SQL Server services"
|
||||
if ($sqlServices.Count -eq 0) {
|
||||
Add-Line "No SQL Server services found."
|
||||
}
|
||||
else {
|
||||
$sqlServices | Format-Table -AutoSize | Out-String | ForEach-Object { Add-Line $_.TrimEnd() }
|
||||
}
|
||||
|
||||
Add-Section "SQL Server live query"
|
||||
Add-Line "sqlcmd path: $(if ($sqlcmdPath) { $sqlcmdPath } else { "not found" })"
|
||||
if (-not $sqlcmdPath) {
|
||||
Add-Line "Cannot query SQL Server live because sqlcmd.exe was not found."
|
||||
}
|
||||
elseif ($queryInstances.Count -eq 0) {
|
||||
Add-Line "No SQL instances to query. Pass -SqlInstance server\instance if needed."
|
||||
}
|
||||
else {
|
||||
foreach ($result in $sqlQueryResults) {
|
||||
Add-Line ""
|
||||
Add-Line "Instance: $($result.Instance)"
|
||||
Add-Line "Success: $($result.Success)"
|
||||
Add-Line $result.Output
|
||||
}
|
||||
}
|
||||
|
||||
$data = [pscustomobject]@{
|
||||
CapturedAt = (Get-Date).ToString("o")
|
||||
ComputerName = $env:COMPUTERNAME
|
||||
UserName = "$env:USERDOMAIN\$env:USERNAME"
|
||||
Windows = [pscustomobject]@{
|
||||
Caption = $os.Caption
|
||||
Version = $os.Version
|
||||
BuildNumber = $os.BuildNumber
|
||||
InstallDate = $os.InstallDate
|
||||
}
|
||||
SageUninstallEntries = $sageEntries
|
||||
SageFileVersions = $sageFileVersions
|
||||
SqlRegistryInstances = $sqlRegistryInstances
|
||||
SqlServices = $sqlServices
|
||||
SqlcmdPath = $sqlcmdPath
|
||||
SqlQueryResults = $sqlQueryResults
|
||||
}
|
||||
|
||||
$reportLines | Set-Content -LiteralPath $reportPath -Encoding UTF8
|
||||
$data | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $jsonPath -Encoding UTF8
|
||||
|
||||
Write-Host "Created:"
|
||||
Write-Host " $reportPath"
|
||||
Write-Host " $jsonPath"
|
||||
Reference in New Issue
Block a user