2019-12-03 08:15:12 +00:00
|
|
|
import frappe from 'frappejs';
|
2019-12-06 18:43:37 +00:00
|
|
|
import fs from 'fs';
|
|
|
|
import { _ } from 'frappejs/utils';
|
2020-01-28 11:10:01 +00:00
|
|
|
import migrate from './migrate';
|
2020-01-02 17:06:57 +00:00
|
|
|
import { remote, shell, ipcRenderer } from 'electron';
|
2020-01-01 08:11:57 +00:00
|
|
|
import SQLite from 'frappejs/backends/sqlite';
|
|
|
|
import postStart from '../server/postStart';
|
2019-12-03 13:10:21 +00:00
|
|
|
import router from '@/router';
|
2019-12-03 10:23:54 +00:00
|
|
|
import Avatar from '@/components/Avatar';
|
2020-01-28 08:20:01 +00:00
|
|
|
import config from '@/config';
|
2019-12-06 18:43:37 +00:00
|
|
|
|
2021-08-24 20:10:16 +00:00
|
|
|
export async function createNewDatabase() {
|
|
|
|
const options = {
|
|
|
|
title: _('Select folder'),
|
|
|
|
defaultPath: 'frappe-books.db'
|
|
|
|
};
|
|
|
|
|
|
|
|
let { filePath } = await remote.dialog.showSaveDialog(options);
|
|
|
|
if (filePath) {
|
|
|
|
if (!filePath.endsWith('.db')) {
|
|
|
|
filePath = filePath + '.db';
|
|
|
|
}
|
|
|
|
if (fs.existsSync(filePath)) {
|
|
|
|
showMessageDialog({
|
|
|
|
// prettier-ignore
|
|
|
|
message: _('A file exists with the same name and it will be overwritten. Are you sure you want to continue?'),
|
|
|
|
buttons: [
|
|
|
|
{
|
|
|
|
label: _('Overwrite'),
|
|
|
|
action() {
|
|
|
|
fs.unlinkSync(filePath);
|
|
|
|
return filePath;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ label: _('Cancel'), action() {} }
|
|
|
|
]
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
return filePath;
|
|
|
|
}
|
|
|
|
}
|
2019-10-26 14:46:04 +00:00
|
|
|
}
|
|
|
|
|
2021-09-04 15:57:58 +00:00
|
|
|
export async function loadExistingDatabase() {
|
|
|
|
const options = {
|
|
|
|
title: _('Select file'),
|
|
|
|
properties: ['openFile'],
|
|
|
|
filters: [{ name: 'SQLite DB File', extensions: ['db'] }]
|
|
|
|
};
|
|
|
|
|
2021-10-25 16:16:12 +00:00
|
|
|
let { filePaths } = await remote.dialog.showOpenDialog(options);
|
|
|
|
|
2021-09-04 15:57:58 +00:00
|
|
|
if (filePaths && filePaths[0]) {
|
2021-10-25 16:16:12 +00:00
|
|
|
return filePaths[0];
|
2021-09-04 15:57:58 +00:00
|
|
|
}
|
2019-10-26 14:46:04 +00:00
|
|
|
}
|
2019-11-27 18:39:16 +00:00
|
|
|
|
2020-01-01 08:11:57 +00:00
|
|
|
export async function connectToLocalDatabase(filepath) {
|
|
|
|
frappe.login('Administrator');
|
|
|
|
frappe.db = new SQLite({
|
|
|
|
dbPath: filepath
|
|
|
|
});
|
|
|
|
await frappe.db.connect();
|
2020-01-28 11:10:01 +00:00
|
|
|
await migrate();
|
2020-01-01 08:11:57 +00:00
|
|
|
await postStart();
|
2020-01-28 08:20:01 +00:00
|
|
|
|
|
|
|
// set file info in config
|
|
|
|
let files = config.get('files') || [];
|
|
|
|
if (!files.find(file => file.filePath === filepath)) {
|
|
|
|
files = [
|
|
|
|
{
|
|
|
|
companyName: frappe.AccountingSettings.companyName,
|
|
|
|
filePath: filepath
|
|
|
|
},
|
|
|
|
...files
|
|
|
|
];
|
|
|
|
config.set('files', files);
|
|
|
|
}
|
|
|
|
|
|
|
|
// set last selected file
|
|
|
|
config.set('lastSelectedFilePath', filepath);
|
2020-01-01 08:11:57 +00:00
|
|
|
}
|
|
|
|
|
2021-08-18 07:01:05 +00:00
|
|
|
export async function showMessageDialog({
|
|
|
|
message,
|
|
|
|
description,
|
|
|
|
buttons = []
|
|
|
|
}) {
|
2019-11-27 18:39:16 +00:00
|
|
|
let buttonLabels = buttons.map(a => a.label);
|
2021-08-18 06:32:45 +00:00
|
|
|
const { response } = await remote.dialog.showMessageBox(
|
2019-11-27 18:39:16 +00:00
|
|
|
remote.getCurrentWindow(),
|
|
|
|
{
|
|
|
|
message,
|
|
|
|
detail: description,
|
|
|
|
buttons: buttonLabels
|
|
|
|
}
|
|
|
|
);
|
2021-08-18 06:32:45 +00:00
|
|
|
|
|
|
|
let button = buttons[response];
|
|
|
|
if (button && button.action) {
|
|
|
|
button.action();
|
|
|
|
}
|
2019-11-27 18:39:16 +00:00
|
|
|
}
|
2019-12-03 08:15:12 +00:00
|
|
|
|
|
|
|
export function deleteDocWithPrompt(doc) {
|
2019-12-20 06:44:31 +00:00
|
|
|
return new Promise(resolve => {
|
2019-12-03 08:15:12 +00:00
|
|
|
showMessageDialog({
|
|
|
|
message: _('Are you sure you want to delete {0} "{1}"?', [
|
|
|
|
doc.doctype,
|
|
|
|
doc.name
|
|
|
|
]),
|
|
|
|
description: _('This action is permanent'),
|
|
|
|
buttons: [
|
|
|
|
{
|
|
|
|
label: _('Delete'),
|
|
|
|
action: () => {
|
|
|
|
doc
|
|
|
|
.delete()
|
|
|
|
.then(() => resolve(true))
|
|
|
|
.catch(e => {
|
2019-12-23 10:37:30 +00:00
|
|
|
handleErrorWithDialog(e, doc);
|
2019-12-03 08:15:12 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: _('Cancel'),
|
|
|
|
action() {
|
|
|
|
resolve(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2019-12-03 10:23:54 +00:00
|
|
|
|
|
|
|
export function partyWithAvatar(party) {
|
|
|
|
return {
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
imageURL: null,
|
|
|
|
label: null
|
|
|
|
};
|
|
|
|
},
|
|
|
|
components: {
|
|
|
|
Avatar
|
|
|
|
},
|
|
|
|
async mounted() {
|
|
|
|
this.imageURL = await frappe.db.getValue('Party', party, 'image');
|
|
|
|
this.label = party;
|
|
|
|
},
|
|
|
|
template: `
|
|
|
|
<div class="flex items-center" v-if="label">
|
|
|
|
<Avatar class="flex-shrink-0" :imageURL="imageURL" :label="label" size="sm" />
|
|
|
|
<span class="ml-2 truncate">{{ label }}</span>
|
|
|
|
</div>
|
|
|
|
`
|
|
|
|
};
|
|
|
|
}
|
2019-12-03 13:10:21 +00:00
|
|
|
|
|
|
|
export function openQuickEdit({ doctype, name, hideFields, defaults = {} }) {
|
|
|
|
let currentRoute = router.currentRoute;
|
2019-12-04 17:26:17 +00:00
|
|
|
let query = currentRoute.query;
|
|
|
|
let method = 'push';
|
|
|
|
if (query.edit && query.doctype === doctype) {
|
|
|
|
// replace the current route if we are
|
|
|
|
// editing another document of the same doctype
|
|
|
|
method = 'replace';
|
|
|
|
}
|
|
|
|
router[method]({
|
2019-12-03 13:10:21 +00:00
|
|
|
query: {
|
|
|
|
edit: 1,
|
|
|
|
doctype,
|
|
|
|
name,
|
|
|
|
hideFields,
|
|
|
|
values: defaults,
|
|
|
|
lastRoute: currentRoute
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2019-12-06 18:43:37 +00:00
|
|
|
|
2020-01-29 11:01:45 +00:00
|
|
|
export function getErrorMessage(e, doc) {
|
2019-12-27 10:23:13 +00:00
|
|
|
let errorMessage = e.message || _('An error occurred');
|
2019-12-23 10:37:30 +00:00
|
|
|
if (e.type === frappe.errors.LinkValidationError) {
|
|
|
|
errorMessage = _('{0} {1} is linked with existing records.', [
|
|
|
|
doc.doctype,
|
|
|
|
doc.name
|
|
|
|
]);
|
|
|
|
} else if (e.type === frappe.errors.DuplicateEntryError) {
|
2019-12-10 09:25:11 +00:00
|
|
|
errorMessage = _('{0} {1} already exists.', [doc.doctype, doc.name]);
|
|
|
|
}
|
2020-01-29 11:01:45 +00:00
|
|
|
return errorMessage;
|
|
|
|
}
|
2019-12-23 10:37:30 +00:00
|
|
|
|
2020-01-29 11:01:45 +00:00
|
|
|
export function handleErrorWithDialog(e, doc) {
|
|
|
|
let errorMessage = getErrorMessage(e, doc);
|
|
|
|
showMessageDialog({ message: errorMessage });
|
2019-12-10 09:25:11 +00:00
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
|
2021-08-18 17:57:39 +00:00
|
|
|
// NOTE: a hack to find all the css from the current document and inject it to the print version
|
|
|
|
// remove this if you are able to fix and get the default css loading on the page
|
|
|
|
function injectCSS(contents) {
|
2021-08-18 17:59:13 +00:00
|
|
|
const styles = document.getElementsByTagName('style');
|
2021-08-18 17:57:39 +00:00
|
|
|
|
2021-08-18 17:59:13 +00:00
|
|
|
for (let style of styles) {
|
2021-08-18 17:57:39 +00:00
|
|
|
contents.insertCSS(style.innerHTML);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-25 18:37:31 +00:00
|
|
|
export async function makePDF(html, destination) {
|
2019-12-06 18:43:37 +00:00
|
|
|
const { BrowserWindow } = remote;
|
|
|
|
|
|
|
|
let printWindow = new BrowserWindow({
|
2019-12-12 17:37:43 +00:00
|
|
|
width: 595,
|
|
|
|
height: 842,
|
2019-12-06 18:43:37 +00:00
|
|
|
show: false,
|
|
|
|
webPreferences: {
|
2021-08-18 17:57:39 +00:00
|
|
|
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
|
|
|
|
enableRemoteModule: true
|
2019-12-06 18:43:37 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-12-12 17:37:43 +00:00
|
|
|
let webpackDevServerURL = remote.getGlobal('WEBPACK_DEV_SERVER_URL');
|
|
|
|
if (webpackDevServerURL) {
|
|
|
|
// Load the url of the dev server if in development mode
|
|
|
|
printWindow.loadURL(webpackDevServerURL + 'print');
|
2019-12-06 18:43:37 +00:00
|
|
|
} else {
|
2019-12-12 17:37:43 +00:00
|
|
|
// Load the index.html when not in development
|
|
|
|
printWindow.loadURL(`app://./print.html`);
|
2019-12-06 18:43:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
printWindow.on('closed', () => {
|
|
|
|
printWindow = null;
|
|
|
|
});
|
|
|
|
|
|
|
|
const code = `
|
|
|
|
document.body.innerHTML = \`${html}\`;
|
|
|
|
`;
|
|
|
|
|
|
|
|
printWindow.webContents.executeJavaScript(code);
|
|
|
|
|
2021-08-18 17:57:39 +00:00
|
|
|
const printOptions = {
|
|
|
|
marginsType: 1, // no margin
|
|
|
|
pageSize: 'A4',
|
|
|
|
printBackground: true,
|
|
|
|
printBackgrounds: true,
|
|
|
|
printSelectionOnly: false
|
|
|
|
};
|
2021-10-25 18:37:31 +00:00
|
|
|
|
|
|
|
const sleep = m => new Promise(r => setTimeout(r, m));
|
|
|
|
|
|
|
|
printWindow.webContents.on('did-finish-load', async () => {
|
|
|
|
injectCSS(printWindow.webContents);
|
|
|
|
await sleep(1000);
|
|
|
|
printWindow.webContents.printToPDF(printOptions).then(data => {
|
|
|
|
printWindow.close();
|
|
|
|
fs.writeFile(destination, data, error => {
|
|
|
|
if (error) throw error;
|
|
|
|
return (shell.openItem(destination));
|
|
|
|
});
|
2019-12-06 18:43:37 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2019-12-20 06:44:31 +00:00
|
|
|
|
|
|
|
export function getActionsForDocument(doc) {
|
|
|
|
if (!doc) return [];
|
|
|
|
|
|
|
|
let deleteAction = {
|
|
|
|
component: {
|
|
|
|
template: `<span class="text-red-700">{{ _('Delete') }}</span>`
|
|
|
|
},
|
2020-02-03 17:50:54 +00:00
|
|
|
condition: doc => !doc.isNew() && !doc.submitted && !doc.meta.isSingle,
|
2019-12-20 06:44:31 +00:00
|
|
|
action: () =>
|
|
|
|
deleteDocWithPrompt(doc).then(res => {
|
|
|
|
if (res) {
|
|
|
|
router.push(`/list/${doc.doctype}`);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
};
|
|
|
|
|
|
|
|
let actions = [...(doc.meta.actions || []), deleteAction]
|
|
|
|
.filter(d => (d.condition ? d.condition(doc) : true))
|
|
|
|
.map(d => {
|
|
|
|
return {
|
|
|
|
label: d.label,
|
|
|
|
component: d.component,
|
|
|
|
action: d.action.bind(this, doc, router)
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
return actions;
|
|
|
|
}
|
2020-01-02 17:06:57 +00:00
|
|
|
|
|
|
|
export function openSettings(tab = 'General') {
|
|
|
|
ipcRenderer.send('open-settings-window', tab);
|
|
|
|
}
|