Commit 88614e13 authored by Nicolas Frandeboeuf's avatar Nicolas Frandeboeuf Committed by Nicolas Frandeboeuf
Browse files

[server] Add CSV export

parent 5414b0c3
Pipeline #318109 passed with stages
in 23 minutes and 10 seconds
......@@ -24,6 +24,7 @@ import DefaultSettings from '../shared/default-settings';
import { cleanData, Remapping } from './helpers';
import { isDemoEnabled } from './settings';
import { ofxToKresus } from './ofx';
import { kresusToCSV } from './csv';
import { IdentifiedRequest } from './routes';
const log = makeLogger('controllers/all');
......@@ -46,7 +47,7 @@ interface ClientAccess {
label?: string | null;
}
interface AllData {
export interface AllData {
accounts: Account[];
accesses: ClientAccess[];
alerts: Alert[];
......@@ -217,6 +218,18 @@ export async function export_(req: IdentifiedRequest<any>, res: express.Response
}
}
export async function exportCSV(req: IdentifiedRequest<any>, res: express.Response) {
try {
const { id: userId } = req.user;
const data = await getAllData(userId, { isExport: true });
res.set('Content-Type', 'text/csv');
res.send(kresusToCSV(data));
} catch (err) {
err.code = ERR_MSG_LOADING_ALL;
asyncErr(res, err, 'when exporting data to CSV');
}
}
type AnyObject = { [key: string]: AnyObject } | any;
function applyRenamings(model: any): (arg: AnyObject) => AnyObject {
......
import { AllData } from './all';
import { assert, displayLabel } from '../helpers';
const COLUMNS_DELIMITER = ';';
const ROWS_DELIMITER = '\n';
function quotesWrapper(text: string): string {
return `"${text}"`;
}
function formatDate(date: Date | null): string {
if (date) {
return date.toISOString().substr(0, 10);
}
return '';
}
export function kresusToCSV(data: AllData): string {
const accessesNamesMap = new Map();
for (const access of data.accesses) {
if (!accessesNamesMap.has(access.vendorId)) {
accessesNamesMap.set(access.vendorId, access.customLabel || access.label);
}
}
const accountsMap = new Map();
for (const account of data.accounts) {
const accountKey = account.id.toString();
if (!accountsMap.has(accountKey)) {
accountsMap.set(accountKey, account);
}
}
const categoriesNamesMap = new Map();
for (const category of data.categories) {
const categoryKey = category.id.toString();
if (!categoriesNamesMap.has(categoryKey)) {
categoriesNamesMap.set(categoryKey, category.label);
}
}
const rows = data.operations.map(transaction => {
const account = accountsMap.get(transaction.accountId.toString());
assert(typeof account !== 'undefined', 'Orphan transaction');
return [
// Access name
account ? accessesNamesMap.get(account.vendorId) || '' : '',
// Account
displayLabel(account) || '',
// Type
transaction.type || '',
// Label
displayLabel(transaction) || '',
// Raw label
transaction.rawLabel || '',
// Date
formatDate(transaction.date),
// Debit date
formatDate(transaction.debitDate),
// Amount
transaction.amount,
// Category
typeof transaction.categoryId === 'number'
? categoriesNamesMap.get(transaction.categoryId.toString()) || ''
: '',
];
});
const columns = [
'Bank',
'Account',
'Type',
'Label',
'Raw label',
'Date',
'Debit date',
'Amount',
'Category',
];
/* eslint-disable indent */
return `${columns.map(quotesWrapper).join(COLUMNS_DELIMITER)}
${rows
.map(row => {
return `${row.map(quotesWrapper).join(COLUMNS_DELIMITER)}`;
})
.join(ROWS_DELIMITER)}`;
/* eslint-enable indent */
}
......@@ -55,6 +55,9 @@ const routes: RoutesDescriptor = {
'all/export': {
post: all.export_,
},
'all/export/csv': {
get: all.exportCSV,
},
// Accesses.
accessId: {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment