2
0
mirror of https://github.com/frappe/books.git synced 2024-12-23 11:29:03 +00:00
books/reports/commonExporter.ts
18alantom e2b9ea1f49 refactor: switch to preload script
- delete ipcCalls
2023-07-10 13:33:52 +05:30

190 lines
4.4 KiB
TypeScript

import { t } from 'fyo';
import { Action } from 'fyo/model/types';
import { Verb } from 'fyo/telemetry/types';
import { getSavePath, showExportInFolder } from 'src/utils/ui';
import { getIsNullOrUndef } from 'utils';
import { generateCSV } from 'utils/csvParser';
import { Report } from './Report';
import { ExportExtention, ReportCell } from './types';
interface JSONExport {
columns: { fieldname: string; label: string }[];
rows: Record<string, unknown>[];
filters: Record<string, string>;
timestamp: string;
reportName: string;
softwareName: string;
softwareVersion: string;
}
export default function getCommonExportActions(report: Report): Action[] {
const exportExtention = ['csv', 'json'] as ExportExtention[];
return exportExtention.map((ext) => ({
group: t`Export`,
label: ext.toUpperCase(),
type: 'primary',
action: async () => {
await exportReport(ext, report);
},
}));
}
async function exportReport(extention: ExportExtention, report: Report) {
const { filePath, canceled } = await getSavePath(
report.reportName,
extention
);
if (canceled || !filePath) {
return;
}
let data = '';
if (extention === 'csv') {
data = getCsvData(report);
} else if (extention === 'json') {
data = getJsonData(report);
}
if (!data.length) {
return;
}
await saveExportData(data, filePath);
report.fyo.telemetry.log(Verb.Exported, report.reportName, { extention });
}
function getJsonData(report: Report): string {
const exportObject: JSONExport = {
columns: [],
rows: [],
filters: {},
timestamp: '',
reportName: '',
softwareName: '',
softwareVersion: '',
};
const columns = report.columns;
const displayPrecision =
(report.fyo.singles.SystemSettings?.displayPrecision as number) ?? 2;
/**
* Set columns as list of fieldname, label
*/
exportObject.columns = columns.map(({ fieldname, label }) => ({
fieldname,
label,
}));
/**
* Set rows as fieldname: value map
*/
for (const row of report.reportData) {
if (row.isEmpty) {
continue;
}
const rowObj: Record<string, unknown> = {};
for (let c = 0; c < row.cells.length; c++) {
const { label } = columns[c];
const cell = getValueFromCell(row.cells[c], displayPrecision);
rowObj[label] = cell;
}
exportObject.rows.push(rowObj);
}
/**
* Set filter map
*/
for (const { fieldname } of report.filters) {
const value = report.get(fieldname);
if (getIsNullOrUndef(value)) {
continue;
}
exportObject.filters[fieldname] = String(value);
}
/**
* Metadata
*/
exportObject.timestamp = new Date().toISOString();
exportObject.reportName = report.reportName;
exportObject.softwareName = 'Frappe Books';
exportObject.softwareVersion = report.fyo.store.appVersion;
return JSON.stringify(exportObject);
}
export function getCsvData(report: Report): string {
const csvMatrix = convertReportToCSVMatrix(report);
return generateCSV(csvMatrix);
}
function convertReportToCSVMatrix(report: Report): unknown[][] {
const displayPrecision =
(report.fyo.singles.SystemSettings?.displayPrecision as number) ?? 2;
const reportData = report.reportData;
const columns = report.columns;
const csvdata: unknown[][] = [];
csvdata.push(columns.map((c) => c.label));
for (const row of reportData) {
if (row.isEmpty) {
csvdata.push(Array(row.cells.length).fill(''));
continue;
}
const csvrow: unknown[] = [];
for (let c = 0; c < row.cells.length; c++) {
const cell = getValueFromCell(row.cells[c], displayPrecision);
csvrow.push(cell);
}
csvdata.push(csvrow);
}
return csvdata;
}
function getValueFromCell(cell: ReportCell, displayPrecision: number) {
const rawValue = cell.rawValue;
if (rawValue instanceof Date) {
return rawValue.toISOString();
}
if (typeof rawValue === 'number') {
const value = rawValue.toFixed(displayPrecision);
/**
* remove insignificant zeroes
*/
if (value.endsWith('0'.repeat(displayPrecision))) {
return value.slice(0, -displayPrecision - 1);
}
return value;
}
if (getIsNullOrUndef(cell)) {
return '';
}
return rawValue;
}
export async function saveExportData(
data: string,
filePath: string,
message?: string
) {
await ipc.saveData(data, filePath);
message ??= t`Export Successful`;
showExportInFolder(message, filePath);
}