fix(accounting): Banana export is now a tab-separated .txt (actually importable)

Banana's "Text file with column headers" import (Actions → Import into
accounting) requires a TAB-separated .txt with unquoted values — picpeak was
emitting a comma-separated, quoted .csv, which won't even show in Banana's
*.txt file picker, let alone parse into columns.

- ledgerService.exportPostings: the `banana` format now serialises TAB-separated
  with no quoting, .txt extension, text/plain content-type. generic + bexio stay
  comma-CSV (RFC 4180). Tab/newline chars in a cell are collapsed to spaces.
- Frontend ledger.service: download filename uses .txt for banana.
- Tests updated for the new banana shape (tab header, .txt, text/plain).

The column names already matched Banana's NameXml; only the serialisation was
wrong. bexio left as comma-CSV (verify against bexio's import spec separately).
This commit is contained in:
Luca
2026-06-15 22:28:10 +02:00
parent b584aaf7a6
commit a19506749a
3 changed files with 32 additions and 10 deletions
@@ -195,11 +195,16 @@ describe('exportPostings', () => {
expect(filename).toMatch(/_generic\.csv$/);
});
it('banana format uses Banana column names', async () => {
const { content, filename } = await ledgerService.exportPostings({ ...period, format: 'banana' });
it('banana format is a TAB-separated .txt with Banana column names', async () => {
const { content, filename, contentType } = await ledgerService.exportPostings({ ...period, format: 'banana' });
const header = content.split('\r\n')[0];
expect(header).toBe('"Date","Doc","Description","AccountDebit","AccountCredit","Amount","VatCode"');
expect(filename).toMatch(/_banana\.csv$/);
// Banana's "Text file with column headers" import wants TAB-separated,
// unquoted values in a .txt — not a comma CSV.
expect(header).toBe('Date\tDoc\tDescription\tAccountDebit\tAccountCredit\tAmount\tVatCode');
expect(content.split('\r\n')[1]).toContain('\t');
expect(content).not.toContain('"');
expect(filename).toMatch(/_banana\.txt$/);
expect(contentType).toMatch(/text\/plain/);
});
it('bexio format includes tax_code + currency', async () => {