Files
accounted/lib/import/shared/workbook-reader.ts
T
Mattsson 81e9dd224e Add/csv import options (#420)
* feat(import): add customer and supplier parsing functionality

- Implemented customer file parsing in `lib/import/customers/parser.ts` with support for Excel and CSV formats.
- Created types for detected customer columns and parsed customer rows in `lib/import/customers/types.ts`.
- Added tests for customer classification logic in `lib/import/shared/__tests__/classify.test.ts`.
- Developed classification functions for customers and suppliers in `lib/import/shared/classify.ts`.
- Introduced shared column utility functions in `lib/import/shared/column-utils.ts`.
- Implemented supplier file parsing in `lib/import/suppliers/parser.ts` with validation for various fields.
- Created types for detected supplier columns and parsed supplier rows in `lib/import/suppliers/types.ts`.
- Added tests for supplier column detection and parsing in `lib/import/suppliers/__tests__/column-detector.test.ts` and `lib/import/suppliers/__tests__/parser.test.ts`.

* fix(labels): update 'Svenskt företag' to 'Svenskt företag eller organisation' for clarity

* feat(import): refactor encoding handling for Swedish files and add tests for character preservation

* feat(recapt): implement clearRecaptIdentity function and integrate into logout flow

* feat(bookkeeping): implement copy functionality and next voucher sequence retrieval

* feat(import): enhance customer and supplier import functionality with normalization and event handling
2026-05-08 15:42:06 +02:00

59 lines
2.0 KiB
TypeScript

import * as XLSX from 'xlsx'
import { decodeFileContent } from './encoding'
/**
* Read a workbook from a raw file buffer, with correct encoding handling
* for CSV files.
*
* For binary spreadsheet formats (.xlsx, .xls, .ods), xlsx handles encoding
* via the embedded codepage and we pass the buffer through as `type: 'array'`.
*
* For CSV files, xlsx with `type: 'array'` decodes bytes as Latin-1, which
* mangles UTF-8 multi-byte sequences (e.g. Ö → Ö). We instead detect the
* source encoding (UTF-8 with optional BOM, or Windows-1252) and decode to
* a string before handing it to xlsx as `type: 'string'`.
*/
export function readWorkbookFromBuffer(buffer: ArrayBuffer, filename: string): XLSX.WorkBook {
const ext = filename.toLowerCase().split('.').pop() ?? ''
if (ext === 'csv') {
const content = decodeFileContent(buffer)
return XLSX.read(content, { type: 'string' })
}
return XLSX.read(buffer, { type: 'array' })
}
/**
* Read the workbook from `buffer` and return raw rows from its largest sheet.
*
* Picks the sheet with the most rows (a heuristic that handles files where
* the header sheet isn't the first one). Returns rows as a 2D string array
* with the header row included; cells default to empty string.
*/
export function readBestSheet(
buffer: ArrayBuffer,
filename: string,
): { sheetName: string; rawData: string[][] } {
const workbook = readWorkbookFromBuffer(buffer, filename)
let bestSheet = workbook.SheetNames[0]
let bestRowCount = 0
for (const name of workbook.SheetNames) {
const sheet = workbook.Sheets[name]
const range = XLSX.utils.decode_range(sheet['!ref'] || 'A1')
const rowCount = range.e.r - range.s.r + 1
if (rowCount > bestRowCount) {
bestRowCount = rowCount
bestSheet = name
}
}
const sheet = workbook.Sheets[bestSheet]
const rawData: string[][] = XLSX.utils.sheet_to_json(sheet, {
header: 1,
defval: '',
raw: false,
})
return { sheetName: bestSheet, rawData }
}