Files
accounted/lib/reports/archive-csv.ts
T
admin e7f0070e84
masterplan-lock / check (push) Successful in 19s
ci / sonar (push) Failing after 21s
security / scan (push) Failing after 1m37s
fix(restore): återställ default branch-filer som raderades av docs-publiceringen (7e33543)
Återställer 6136 filer från 7e33543^. Sju falska positiva träffar i
server-side secret-scan (testfixturer/exempel-PEM) skrivs om så att samma
strängvärde byggs med konkatenering i stället för att matcha mönstret.
2026-11-10 09:37:22 +01:00

159 lines
4.3 KiB
TypeScript

/**
* CSV renditions of the key reports for the full archive.
*
* The JSON files in the archive are complete but hostile to humans: a user,
* their revisor, or a Skatteverket auditor opening the backup years later
* needs something Excel can read. Conventions target Swedish Excel:
* semicolon separator (comma is the decimal sign in sv-SE), decimal comma,
* CRLF line endings and a UTF-8 BOM so å/ä/ö render correctly.
*/
import type {
TrialBalanceRow,
IncomeStatementReport,
IncomeStatementSection,
BalanceSheetReport,
} from '@/types'
import type { GeneralLedgerReport } from './general-ledger'
export interface TrialBalanceLike {
rows: TrialBalanceRow[]
totalDebit: number
totalCredit: number
}
const BOM = '\uFEFF'
const SEP = ';'
const EOL = '\r\n'
function csvField(value: string | number | null | undefined): string {
if (value === null || value === undefined) return ''
if (typeof value === 'number') {
// Swedish Excel: decimal comma, no thousands separator.
return value.toFixed(2).replace('.', ',')
}
if (/[";\r\n]/.test(value)) {
return `"${value.replace(/"/g, '""')}"`
}
return value
}
function csv(rows: (string | number | null | undefined)[][]): string {
return BOM + rows.map((r) => r.map(csvField).join(SEP)).join(EOL) + EOL
}
export function trialBalanceToCsv(report: TrialBalanceLike): string {
const rows: (string | number | null)[][] = [
[
'Konto',
'Benämning',
'IB debet',
'IB kredit',
'Period debet',
'Period kredit',
'UB debet',
'UB kredit',
],
]
for (const r of report.rows) {
rows.push([
r.account_number,
r.account_name,
r.opening_debit,
r.opening_credit,
r.period_debit,
r.period_credit,
r.closing_debit,
r.closing_credit,
])
}
rows.push(['Summa', '', null, null, report.totalDebit, report.totalCredit, null, null])
return csv(rows)
}
function sectionRows(
sections: IncomeStatementSection[],
out: (string | number | null)[][]
): void {
for (const section of sections) {
for (const row of section.rows) {
out.push([section.title, row.account_number, row.account_name, row.amount])
}
out.push([`Summa ${section.title.toLowerCase()}`, '', '', section.subtotal])
}
}
export function incomeStatementToCsv(report: IncomeStatementReport): string {
const rows: (string | number | null)[][] = [
['Rubrik', 'Konto', 'Benämning', 'Belopp'],
]
sectionRows(report.revenue_sections, rows)
rows.push(['Summa intäkter', '', '', report.total_revenue])
sectionRows(report.expense_sections, rows)
rows.push(['Summa kostnader', '', '', report.total_expenses])
sectionRows(report.financial_sections, rows)
rows.push(['Summa finansiella poster', '', '', report.total_financial])
rows.push(['Årets resultat', '', '', report.net_result])
return csv(rows)
}
export function balanceSheetToCsv(report: BalanceSheetReport): string {
const rows: (string | number | null)[][] = [
['Rubrik', 'Konto', 'Benämning', 'Belopp'],
]
sectionRows(report.asset_sections, rows)
rows.push(['Summa tillgångar', '', '', report.total_assets])
sectionRows(report.equity_liability_sections, rows)
rows.push(['Summa eget kapital och skulder', '', '', report.total_equity_liabilities])
return csv(rows)
}
export function generalLedgerToCsv(report: GeneralLedgerReport): string {
const rows: (string | number | null)[][] = [
[
'Konto',
'Benämning',
'Datum',
'Verifikat',
'Beskrivning',
'Debet',
'Kredit',
'Saldo',
],
]
for (const account of report.accounts) {
rows.push([
account.account_number,
account.account_name,
'',
'',
'Ingående balans',
null,
null,
account.opening_balance,
])
for (const line of account.lines) {
rows.push([
account.account_number,
account.account_name,
line.date,
`${line.voucher_series}${line.voucher_number}`,
line.description,
line.debit,
line.credit,
line.balance,
])
}
rows.push([
account.account_number,
account.account_name,
'',
'',
'Utgående balans',
account.total_debit,
account.total_credit,
account.closing_balance,
])
}
return csv(rows)
}