feat(accounting): add a Banana "Income & Expense" (cash-book) export format

The Banana export assumed a double-entry file; a user importing into an Income
& Expense (Einnahmen-Ausgaben) file got "AccountDebit/AccountCredit/Amount/
VatCode column not found", since those columns only exist in double-entry.

Add a second Banana format alongside the double-entry one:
- ledgerService: new `banana_ie` format → Banana I&E columns Date, Doc,
  Description, Income, Expenses, ContraAccount (the income/expense account),
  VatCode (banana.ch doc 9946). Revenue → gross in Income + revenue account;
  cost → gross in Expenses + expense account. Same tab-separated .txt shape.
- Frontend: ExportFormat + dropdown gain `banana_ie`; .txt extension covers
  both Banana variants. Labels relabelled: "Banana — double-entry" and
  "Banana — income & expense" (de equivalents). Hint de-"double-entry"-fied.
- Test added for the I&E format.

Pairs with the prior UTF-8 BOM fix (the "·" mojibake). Tests + build green.
This commit is contained in:
Luca
2026-06-15 22:48:06 +02:00
parent 74144da45f
commit 445d6d7b6d
6 changed files with 51 additions and 17 deletions
+4 -4
View File
@@ -8,7 +8,7 @@ import { api } from '../config/api';
export type AccountType = 'asset' | 'liability' | 'equity' | 'revenue' | 'expense';
export type VatDirection = 'output' | 'input';
export type ExportFormat = 'generic' | 'banana' | 'bexio';
export type ExportFormat = 'generic' | 'banana' | 'banana_ie' | 'bexio';
export interface LedgerAccount {
id: number;
@@ -92,9 +92,9 @@ export const ledgerService = {
const usp = new URLSearchParams({ from: params.from, to: params.to, currency: params.currency, format: params.format });
const res = await api.get(`/admin/ledger/export?${usp.toString()}`, { responseType: 'blob' });
const url = URL.createObjectURL(res.data);
// Banana wants a tab-separated .txt (its "Text file with column headers"
// import); generic / bexio stay .csv. Matches the backend's extension.
const ext = params.format === 'banana' ? 'txt' : 'csv';
// Both Banana variants want a tab-separated .txt (its "Text file with column
// headers" import); generic / bexio stay .csv. Matches the backend extension.
const ext = (params.format === 'banana' || params.format === 'banana_ie') ? 'txt' : 'csv';
const filename = `journal_${params.from}_to_${params.to}_${params.currency}_${params.format}.${ext}`;
return { url, filename };
},