diff --git a/reports/GoodsAndServiceTax/gstExporter.ts b/reports/GoodsAndServiceTax/gstExporter.ts index 972fc5dd..1a7efc3f 100644 --- a/reports/GoodsAndServiceTax/gstExporter.ts +++ b/reports/GoodsAndServiceTax/gstExporter.ts @@ -5,11 +5,11 @@ import { ModelNameEnum } from 'models/types'; import { codeStateMap } from 'regional/in'; import { ExportExtention } from 'reports/types'; import { showDialog } from 'src/utils/interactive'; -import { getSavePath } from 'src/utils/ipcCalls'; import { invertMap } from 'utils'; import { getCsvData, saveExportData } from '../commonExporter'; import { BaseGSTR } from './BaseGSTR'; import { TransferTypeEnum } from './types'; +import { getSavePath } from 'src/utils/ui'; const GST = { 'GST-0': 0, diff --git a/reports/commonExporter.ts b/reports/commonExporter.ts index ea55aa5d..cf18c307 100644 --- a/reports/commonExporter.ts +++ b/reports/commonExporter.ts @@ -1,11 +1,12 @@ import { t } from 'fyo'; import { Action } from 'fyo/model/types'; import { Verb } from 'fyo/telemetry/types'; -import { getSavePath, saveData, showExportInFolder } from 'src/utils/ipcCalls'; +import { saveData } from 'src/utils/ipcCalls'; import { getIsNullOrUndef } from 'utils'; import { generateCSV } from 'utils/csvParser'; import { Report } from './Report'; import { ExportExtention, ReportCell } from './types'; +import { getSavePath, showExportInFolder } from 'src/utils/ui'; interface JSONExport { columns: { fieldname: string; label: string }[]; diff --git a/src/App.vue b/src/App.vue index 9f7e9883..7da7d944 100644 --- a/src/App.vue +++ b/src/App.vue @@ -54,6 +54,7 @@ import { initializeInstance } from './utils/initialization'; import * as injectionKeys from './utils/injectionKeys'; import { showDialog } from './utils/interactive'; import { checkDbAccess, checkForUpdates } from './utils/ipcCalls'; +import { setLanguageMap } from './utils/language'; import { updateConfigFiles } from './utils/misc'; import { updatePrintTemplates } from './utils/printTemplates'; import { Search } from './utils/search'; @@ -147,6 +148,7 @@ export default defineComponent({ await this.setDeskRoute(); await fyo.telemetry.start(true); await checkForUpdates(); + await setLanguageMap(); this.dbPath = filePath; this.companyName = (await fyo.getValue( ModelNameEnum.AccountingSettings, diff --git a/src/components/ExportWizard.vue b/src/components/ExportWizard.vue index 3f298fa8..91ec68b2 100644 --- a/src/components/ExportWizard.vue +++ b/src/components/ExportWizard.vue @@ -95,7 +95,7 @@ import { getExportTableFields, getJsonExportData, } from 'src/utils/export'; -import { getSavePath, saveData, showExportInFolder } from 'src/utils/ipcCalls'; +import { saveData } from 'src/utils/ipcCalls'; import { ExportField, ExportFormat, ExportTableField } from 'src/utils/types'; import { QueryFilter } from 'utils/db/types'; import { defineComponent, PropType } from 'vue'; @@ -105,6 +105,7 @@ import Int from './Controls/Int.vue'; import Select from './Controls/Select.vue'; import FormHeader from './FormHeader.vue'; import { Verb } from 'fyo/telemetry/types'; +import { getSavePath, showExportInFolder } from 'src/utils/ui'; interface ExportWizardData { useListFilters: boolean; diff --git a/src/pages/DatabaseSelector.vue b/src/pages/DatabaseSelector.vue index f0687421..5944c316 100644 --- a/src/pages/DatabaseSelector.vue +++ b/src/pages/DatabaseSelector.vue @@ -246,13 +246,9 @@ import Loading from 'src/components/Loading.vue'; import Modal from 'src/components/Modal.vue'; import { fyo } from 'src/initFyo'; import { showDialog } from 'src/utils/interactive'; -import { - deleteDb, - getDbList, - getSavePath, - getSelectedFilePath, -} from 'src/utils/ipcCalls'; +import { getDbList } from 'src/utils/ipcCalls'; import { updateConfigFiles } from 'src/utils/misc'; +import { deleteDb, getSavePath, getSelectedFilePath } from 'src/utils/ui'; import type { ConfigFilesWithModified } from 'utils/types'; import { defineComponent } from 'vue'; diff --git a/src/pages/ImportWizard.vue b/src/pages/ImportWizard.vue index daca6647..f071d197 100644 --- a/src/pages/ImportWizard.vue +++ b/src/pages/ImportWizard.vue @@ -388,10 +388,10 @@ import PageHeader from 'src/components/PageHeader.vue'; import { Importer, TemplateField, getColumnLabel } from 'src/importer'; import { fyo } from 'src/initFyo'; import { showDialog } from 'src/utils/interactive'; -import { getSavePath, saveData } from 'src/utils/ipcCalls'; +import { saveData } from 'src/utils/ipcCalls'; import { docsPathMap } from 'src/utils/misc'; import { docsPathRef } from 'src/utils/refs'; -import { selectTextFile } from 'src/utils/ui'; +import { getSavePath, selectTextFile } from 'src/utils/ui'; import { defineComponent } from 'vue'; import Loading from '../components/Loading.vue'; diff --git a/src/pages/TemplateBuilder/TemplateBuilder.vue b/src/pages/TemplateBuilder/TemplateBuilder.vue index d2723f06..a8b4d373 100644 --- a/src/pages/TemplateBuilder/TemplateBuilder.vue +++ b/src/pages/TemplateBuilder/TemplateBuilder.vue @@ -233,23 +233,23 @@ import ShortcutKeys from 'src/components/ShortcutKeys.vue'; import { handleErrorWithDialog } from 'src/errorHandling'; import { shortcutsKey } from 'src/utils/injectionKeys'; import { showDialog, showToast } from 'src/utils/interactive'; -import { getSavePath } from 'src/utils/ipcCalls'; import { docsPathMap } from 'src/utils/misc'; import { - PrintTemplateHint, - baseTemplate, - getPrintTemplatePropHints, - getPrintTemplatePropValues, +PrintTemplateHint, +baseTemplate, +getPrintTemplatePropHints, +getPrintTemplatePropValues, } from 'src/utils/printTemplates'; import { docsPathRef, showSidebar } from 'src/utils/refs'; import { DocRef, PrintValues } from 'src/utils/types'; import { - ShortcutKey, - focusOrSelectFormControl, - getActionsForDoc, - getDocFromNameIfExistsElseNew, - openSettings, - selectTextFile, +ShortcutKey, +focusOrSelectFormControl, +getActionsForDoc, +getDocFromNameIfExistsElseNew, +getSavePath, +openSettings, +selectTextFile, } from 'src/utils/ui'; import { useDocShortcuts } from 'src/utils/vueUtils'; import { getMapFromList } from 'utils/index'; diff --git a/src/utils/ipcCalls.ts b/src/utils/ipcCalls.ts index 842a18c0..8b9612ad 100644 --- a/src/utils/ipcCalls.ts +++ b/src/utils/ipcCalls.ts @@ -2,8 +2,12 @@ * Utils that make ipcRenderer calls. */ const { ipcRenderer } = require('electron'); -import { t } from 'fyo'; -import { BaseError } from 'fyo/utils/errors'; +import type { + OpenDialogOptions, + OpenDialogReturnValue, + SaveDialogOptions, + SaveDialogReturnValue, +} from 'electron'; import type { BackendResponse } from 'utils/ipc/types'; import { IPC_ACTIONS, IPC_CHANNELS, IPC_MESSAGES } from 'utils/messages'; import type { @@ -13,9 +17,6 @@ import type { SelectFileReturn, TemplateFile, } from 'utils/types'; -import { showDialog, showToast } from './interactive'; -import { setLanguageMap } from './language'; -import type { OpenDialogReturnValue } from 'electron'; export function reloadWindow() { return ipcRenderer.send(IPC_MESSAGES.RELOAD_MAIN_WINDOW); @@ -29,12 +30,11 @@ export async function getLanguageMap(code: string) { }; } -export async function getSelectedFilePath(): Promise { - return (await ipcRenderer.invoke(IPC_ACTIONS.GET_OPEN_FILEPATH, { - title: t`Select file`, - properties: ['openFile'], - filters: [{ name: 'SQLite DB File', extensions: ['db'] }], - })) as OpenDialogReturnValue; +export async function getOpenFilePath(options: OpenDialogOptions) { + return (await ipcRenderer.invoke( + IPC_ACTIONS.GET_OPEN_FILEPATH, + options + )) as OpenDialogReturnValue; } export async function getTemplates(): Promise { @@ -61,43 +61,17 @@ export async function checkDbAccess(filePath: string) { export async function checkForUpdates() { await ipcRenderer.invoke(IPC_ACTIONS.CHECK_FOR_UPDATES); - await setLanguageMap(); } export function openLink(link: string) { ipcRenderer.send(IPC_MESSAGES.OPEN_EXTERNAL, link); } -export async function deleteDb(filePath: string) { - const { error } = (await ipcRenderer.invoke( +export async function deleteFile(filePath: string) { + return (await ipcRenderer.invoke( IPC_ACTIONS.DELETE_FILE, filePath )) as BackendResponse; - - if (error?.code === 'EBUSY') { - await showDialog({ - title: t`Delete Failed`, - detail: t`Please restart and try again.`, - type: 'error', - }); - } else if (error?.code === 'ENOENT') { - await showDialog({ - title: t`Delete Failed`, - detail: t`File ${filePath} does not exist.`, - type: 'error', - }); - } else if (error?.code === 'EPERM') { - await showDialog({ - title: t`Cannot Delete`, - detail: t`Close Frappe Books and try manually.`, - type: 'error', - }); - } else if (error) { - const err = new BaseError(500, error.message); - err.name = error.name; - err.stack = error.stack; - throw err; - } } export async function saveData(data: string, savePath: string) { @@ -113,47 +87,21 @@ export async function makePDF( savePath: string, width: number, height: number -): Promise { - const success = (await ipcRenderer.invoke( +): Promise { + return (await ipcRenderer.invoke( IPC_ACTIONS.SAVE_HTML_AS_PDF, html, savePath, width, height )) as boolean; - - if (success) { - showExportInFolder(t`Save as PDF Successful`, savePath); - } else { - showToast({ message: t`Export Failed`, type: 'error' }); - } } -export function showExportInFolder(message: string, filePath: string) { - showToast({ - message, - actionText: t`Open Folder`, - type: 'success', - action: () => { - showItemInFolder(filePath); - }, - }); -} - -export async function getSavePath(name: string, extention: string) { - const response = (await ipcRenderer.invoke(IPC_ACTIONS.GET_SAVE_FILEPATH, { - title: t`Select Folder`, - defaultPath: `${name}.${extention}`, - })) as { canceled: boolean; filePath?: string }; - - const canceled = response.canceled; - let filePath = response.filePath; - - if (filePath && !filePath.endsWith(extention) && filePath !== ':memory:') { - filePath = `${filePath}.${extention}`; - } - - return { canceled, filePath }; +export async function getSaveFilePath(options: SaveDialogOptions) { + return (await ipcRenderer.invoke( + IPC_ACTIONS.GET_SAVE_FILEPATH, + options + )) as SaveDialogReturnValue; } export async function getDbList() { diff --git a/src/utils/printTemplates.ts b/src/utils/printTemplates.ts index f59bce6e..3fc7f8ec 100644 --- a/src/utils/printTemplates.ts +++ b/src/utils/printTemplates.ts @@ -1,13 +1,18 @@ -import { Fyo } from 'fyo'; +import { Fyo, t } from 'fyo'; import { Doc } from 'fyo/model/doc'; import { Invoice } from 'models/baseModels/Invoice/Invoice'; import { ModelNameEnum } from 'models/types'; import { FieldTypeEnum, Schema, TargetField } from 'schemas/types'; import { getValueMapFromList } from 'utils/index'; import { TemplateFile } from 'utils/types'; -import { getSavePath, getTemplates, makePDF } from './ipcCalls'; +import { showToast } from './interactive'; +import { getTemplates, makePDF } from './ipcCalls'; import { PrintValues } from './types'; -import { getDocFromNameIfExistsElseNew } from './ui'; +import { + getDocFromNameIfExistsElseNew, + getSavePath, + showExportInFolder, +} from './ui'; export type PrintTemplateHint = { [key: string]: string | PrintTemplateHint | PrintTemplateHint[]; @@ -223,13 +228,18 @@ export async function getPathAndMakePDF( width: number, height: number ) { - const { filePath } = await getSavePath(name, 'pdf'); - if (!filePath) { + const { filePath: savePath } = await getSavePath(name, 'pdf'); + if (!savePath) { return; } const html = constructPrintDocument(innerHTML); - await makePDF(html, filePath, width, height); + const success = await makePDF(html, savePath, width, height); + if (success) { + showExportInFolder(t`Save as PDF Successful`, savePath); + } else { + showToast({ message: t`Export Failed`, type: 'error' }); + } } function constructPrintDocument(innerHTML: string) { diff --git a/src/utils/ui.ts b/src/utils/ui.ts index 6b6087da..b5e18d75 100644 --- a/src/utils/ui.ts +++ b/src/utils/ui.ts @@ -6,7 +6,12 @@ import { t } from 'fyo'; import type { Doc } from 'fyo/model/doc'; import { Action } from 'fyo/model/types'; import { getActions } from 'fyo/utils'; -import { getDbError, LinkValidationError, ValueError } from 'fyo/utils/errors'; +import { + BaseError, + getDbError, + LinkValidationError, + ValueError, +} from 'fyo/utils/errors'; import { Invoice } from 'models/baseModels/Invoice/Invoice'; import { PurchaseInvoice } from 'models/baseModels/PurchaseInvoice/PurchaseInvoice'; import { SalesInvoice } from 'models/baseModels/SalesInvoice/SalesInvoice'; @@ -18,11 +23,18 @@ import { Schema } from 'schemas/types'; import { handleErrorWithDialog } from 'src/errorHandling'; import { fyo } from 'src/initFyo'; import router from 'src/router'; +import { assertIsType } from 'utils/index'; import { SelectFileOptions } from 'utils/types'; import { RouteLocationRaw } from 'vue-router'; import { evaluateHidden } from './doc'; import { showDialog, showToast } from './interactive'; -import { selectFile } from './ipcCalls'; +import { + deleteFile, + getOpenFilePath, + getSaveFilePath, + selectFile, + showItemInFolder, +} from './ipcCalls'; import { showSidebar } from './refs'; import { ActionGroup, @@ -31,7 +43,6 @@ import { ToastOptions, UIGroupedFields, } from './types'; -import { assertIsType } from 'utils/index'; export const toastDurationMap = { short: 2_500, long: 5_000 } as const; @@ -953,3 +964,67 @@ export const paperSizeMap: Record< height: -1, }, }; + +export function showExportInFolder(message: string, filePath: string) { + showToast({ + message, + actionText: t`Open Folder`, + type: 'success', + action: () => { + showItemInFolder(filePath); + }, + }); +} + +export async function deleteDb(filePath: string) { + const { error } = await deleteFile(filePath); + + if (error?.code === 'EBUSY') { + await showDialog({ + title: t`Delete Failed`, + detail: t`Please restart and try again.`, + type: 'error', + }); + } else if (error?.code === 'ENOENT') { + await showDialog({ + title: t`Delete Failed`, + detail: t`File ${filePath} does not exist.`, + type: 'error', + }); + } else if (error?.code === 'EPERM') { + await showDialog({ + title: t`Cannot Delete`, + detail: t`Close Frappe Books and try manually.`, + type: 'error', + }); + } else if (error) { + const err = new BaseError(500, error.message); + err.name = error.name; + err.stack = error.stack; + throw err; + } +} + +export async function getSelectedFilePath() { + return getOpenFilePath({ + title: t`Select file`, + properties: ['openFile'], + filters: [{ name: 'SQLite DB File', extensions: ['db'] }], + }); +} + +export async function getSavePath(name: string, extention: string) { + const response = await getSaveFilePath({ + title: t`Select folder`, + defaultPath: `${name}.${extention}`, + }); + + const canceled = response.canceled; + let filePath = response.filePath; + + if (filePath && !filePath.endsWith(extention) && filePath !== ':memory:') { + filePath = `${filePath}.${extention}`; + } + + return { canceled, filePath }; +}