mirror of
https://github.com/frappe/books.git
synced 2024-12-23 11:29:03 +00:00
Merge pull request #220 from 18alantom/fix-reported-issues-and-improvements
fix: reported issues and improvements
This commit is contained in:
commit
c8732d6d8d
@ -97,8 +97,23 @@ module.exports = {
|
||||
return doc.getFrom('Address', doc.address, 'addressDisplay');
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldname: 'gstin',
|
||||
label: 'GSTIN No.',
|
||||
fieldtype: 'Data',
|
||||
hidden: form => {
|
||||
return form.gstType === 'Registered Regular' ? 0 : 1;
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
quickEditFields: ['email', 'phone', 'address', 'defaultAccount', 'currency']
|
||||
quickEditFields: [
|
||||
'email',
|
||||
'phone',
|
||||
'address',
|
||||
'defaultAccount',
|
||||
'currency',
|
||||
'gstin'
|
||||
]
|
||||
};
|
||||
|
@ -102,7 +102,7 @@ module.exports = {
|
||||
label: 'Amount',
|
||||
fieldtype: 'Currency',
|
||||
required: 1,
|
||||
default: doc => doc.getSum('for', 'amount')
|
||||
formula: doc => doc.getSum('for', 'amount')
|
||||
},
|
||||
{
|
||||
fieldname: 'writeoff',
|
||||
|
@ -19,6 +19,9 @@ export default {
|
||||
computed: {
|
||||
partyField() {
|
||||
return this.doc.doctype === 'SalesInvoice' ? 'customer' : 'supplier';
|
||||
},
|
||||
isSalesInvoice() {
|
||||
return this.doc.doctype === 'SalesInvoice';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="border h-full">
|
||||
<div>
|
||||
<div class="px-6 pt-6" v-if="printSettings && accountingSettings">
|
||||
<div class="px-6 pt-6" v-if="printSettings">
|
||||
<div class="flex text-sm text-gray-900 border-b pb-4">
|
||||
<div class="w-1/3">
|
||||
<div v-if="printSettings.displayLogo">
|
||||
@ -11,7 +11,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="text-xl text-gray-700 font-semibold" v-else>
|
||||
{{ accountingSettings.companyName }}
|
||||
{{ frappe.AccountingSettings.companyName }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/3">
|
||||
@ -19,7 +19,10 @@
|
||||
<div class="mt-1">{{ printSettings.phone }}</div>
|
||||
</div>
|
||||
<div class="w-1/3">
|
||||
<div v-if="address">{{ address.addressDisplay }}</div>
|
||||
<div v-if="companyAddress">{{ companyAddress.addressDisplay }}</div>
|
||||
<div v-if="printSettings && printSettings.gstin">
|
||||
GSTIN: {{ printSettings.gstin }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -33,60 +36,62 @@
|
||||
{{ frappe.format(doc.date, 'Date') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/3">
|
||||
<div class="w-1/3" v-if="party">
|
||||
<div class="py-1 text-right text-lg font-semibold">
|
||||
{{ doc[partyField.fieldname] }}
|
||||
</div>
|
||||
<div v-if="partyDoc" class="mt-1 text-xs text-gray-600 text-right">
|
||||
{{ partyDoc.addressDisplay }}
|
||||
{{ party.name }}
|
||||
</div>
|
||||
<div
|
||||
v-if="partyDoc && partyDoc.gstin"
|
||||
v-if="party && party.addressDisplay"
|
||||
class="mt-1 text-xs text-gray-600 text-right"
|
||||
>
|
||||
GSTIN: {{ partyDoc.gstin }}
|
||||
{{ party.addressDisplay }}
|
||||
</div>
|
||||
<div
|
||||
v-if="party && party.gstin"
|
||||
class="mt-1 text-xs text-gray-600 text-right"
|
||||
>
|
||||
GSTIN: {{ party.gstin }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2 px-6 text-base">
|
||||
<div>
|
||||
<Row class="text-gray-600 w-full" :ratio="ratio">
|
||||
<div class="py-4">
|
||||
{{ _('No') }}
|
||||
</div>
|
||||
<div
|
||||
class="py-4"
|
||||
v-for="df in itemFields"
|
||||
:key="df.fieldname"
|
||||
:class="textAlign(df)"
|
||||
>
|
||||
{{ df.label }}
|
||||
</div>
|
||||
</Row>
|
||||
<Row
|
||||
class="text-gray-900 w-full"
|
||||
<div class="text-gray-600 w-full flex border-b">
|
||||
<div class="py-4 w-5/12">Item</div>
|
||||
<div class="py-4 text-right w-1/12">Quantity</div>
|
||||
<div class="py-4 text-right w-3/12">Rate</div>
|
||||
<div class="py-4 text-right w-3/12">Amount</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex py-1 text-gray-900 w-full border-b"
|
||||
v-for="row in doc.items"
|
||||
:key="row.name"
|
||||
:ratio="ratio"
|
||||
>
|
||||
<div class="py-4">
|
||||
{{ row.idx + 1 }}
|
||||
<div class="w-5/12 py-4">{{ row.item }}</div>
|
||||
<div class="w-1/12 text-right py-4">
|
||||
{{ format(row, 'quantity') }}
|
||||
</div>
|
||||
<div
|
||||
class="py-4"
|
||||
v-for="df in itemFields"
|
||||
:key="df.fieldname"
|
||||
:class="textAlign(df)"
|
||||
>
|
||||
{{ frappe.format(row[df.fieldname], df) }}
|
||||
<div class="w-3/12 text-right py-4">{{ format(row, 'rate') }}</div>
|
||||
<div class="w-3/12 text-right py-4">
|
||||
{{ format(row, 'amount') }}
|
||||
</div>
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-6 mt-2 flex justify-end text-base">
|
||||
<div class="w-64">
|
||||
<div class="w-1/2 bg-pink">
|
||||
<div
|
||||
class="uppercase text-sm tracking-widest font-semibold text-gray-800 mt-2"
|
||||
>
|
||||
Notes
|
||||
</div>
|
||||
<div class="my-4 text-lg whitespace-pre-line">
|
||||
{{ doc.terms }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
<div class="flex pl-2 justify-between py-3 border-b">
|
||||
<div>{{ _('Subtotal') }}</div>
|
||||
<div>{{ frappe.format(doc.netTotal, 'Currency') }}</div>
|
||||
@ -111,57 +116,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import frappe from 'frappejs';
|
||||
import Row from '@/components/Row';
|
||||
import Base from './Base';
|
||||
|
||||
export default {
|
||||
name: 'Default',
|
||||
props: ['doc', 'printSettings'],
|
||||
components: {
|
||||
Row
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
accountingSettings: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
meta() {
|
||||
return this.doc && this.doc.meta;
|
||||
},
|
||||
address() {
|
||||
return this.printSettings && this.printSettings.getLink('address');
|
||||
},
|
||||
partyDoc() {
|
||||
return this.doc.getLink(this.partyField.fieldname);
|
||||
},
|
||||
partyField() {
|
||||
let fieldname = {
|
||||
SalesInvoice: 'customer',
|
||||
PurchaseInvoice: 'supplier'
|
||||
}[this.doc.doctype];
|
||||
return this.meta.getField(fieldname);
|
||||
},
|
||||
itemFields() {
|
||||
let itemsMeta = frappe.getMeta(`${this.doc.doctype}Item`);
|
||||
return ['item', 'quantity', 'rate', 'amount'].map(fieldname =>
|
||||
itemsMeta.getField(fieldname)
|
||||
);
|
||||
},
|
||||
ratio() {
|
||||
return [0.3].concat(this.itemFields.map(() => 1));
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
textAlign(df) {
|
||||
return ['Currency', 'Int', 'Float'].includes(df.fieldtype)
|
||||
? 'text-right'
|
||||
: '';
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.accountingSettings = await frappe.getSingle('AccountingSettings');
|
||||
await this.doc.loadLink(this.partyField.fieldname);
|
||||
}
|
||||
extends: Base,
|
||||
props: ['doc', 'printSettings']
|
||||
};
|
||||
</script>
|
||||
|
@ -23,12 +23,18 @@
|
||||
<div class="text-sm text-gray-800" v-if="companyAddress">
|
||||
{{ companyAddress.addressDisplay }}
|
||||
</div>
|
||||
<div
|
||||
class="text-sm text-gray-800"
|
||||
v-if="printSettings && printSettings.gstin"
|
||||
>
|
||||
GSTIN: {{ printSettings.gstin }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-8 text-lg">
|
||||
<div class="flex">
|
||||
<div class="w-1/3 font-semibold">
|
||||
{{ doc.doctype === 'SalesInvoice' ? 'Invoice' : 'Bill' }}
|
||||
{{ isSalesInvoice ? 'Invoice' : 'Bill' }}
|
||||
</div>
|
||||
<div class="w-2/3 text-gray-800">
|
||||
<div class="font-semibold">
|
||||
@ -41,7 +47,7 @@
|
||||
</div>
|
||||
<div class="mt-4 flex">
|
||||
<div class="w-1/3 font-semibold">
|
||||
{{ doc.doctype === 'SalesInvoice' ? 'Customer' : 'Supplier' }}
|
||||
{{ isSalesInvoice ? 'Customer' : 'Supplier' }}
|
||||
</div>
|
||||
<div class="w-2/3 text-gray-800" v-if="party">
|
||||
<div class="font-semibold">
|
||||
@ -50,6 +56,7 @@
|
||||
<div>
|
||||
{{ party.addressDisplay }}
|
||||
</div>
|
||||
<div v-if="party && party.gstin">GSTIN: {{ party.gstin }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -42,22 +42,34 @@
|
||||
<div
|
||||
class="uppercase text-sm font-semibold tracking-widest text-gray-800"
|
||||
>
|
||||
To
|
||||
{{ isSalesInvoice ? 'To' : 'From' }}
|
||||
</div>
|
||||
<div class="mt-4 text-black leading-relaxed text-lg">
|
||||
{{ party.name }} <br />
|
||||
{{ party.addressDisplay }}
|
||||
</div>
|
||||
<div
|
||||
class="mt-4 text-black leading-relaxed text-lg"
|
||||
v-if="party && party.gstin"
|
||||
>
|
||||
GSTIN: {{ party.gstin }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-1/2" v-if="companyAddress">
|
||||
<div
|
||||
class="uppercase text-sm font-semibold tracking-widest text-gray-800"
|
||||
class="uppercase text-sm font-semibold tracking-widest text-gray-800 ml-8"
|
||||
>
|
||||
From
|
||||
{{ isSalesInvoice ? 'From' : 'To' }}
|
||||
</div>
|
||||
<div class="mt-4 text-black leading-relaxed text-lg">
|
||||
<div class="mt-4 ml-8 text-black leading-relaxed text-lg">
|
||||
{{ companyAddress.addressDisplay }}
|
||||
</div>
|
||||
<div
|
||||
class="mt-4 ml-8 text-black leading-relaxed text-lg"
|
||||
v-if="printSettings && printSettings.gstin"
|
||||
>
|
||||
GSTIN: {{ printSettings.gstin }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-12 py-10 border-b">
|
||||
|
@ -25,14 +25,15 @@ module.exports = {
|
||||
label: 'Description',
|
||||
fieldtype: 'Text',
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'description'),
|
||||
hidden: 1
|
||||
hidden: 1,
|
||||
formulaDependsOn: ['item']
|
||||
},
|
||||
{
|
||||
fieldname: 'quantity',
|
||||
label: 'Quantity',
|
||||
fieldtype: 'Float',
|
||||
required: 1,
|
||||
formula: () => 1
|
||||
formula: row => row.quantity || 1
|
||||
},
|
||||
{
|
||||
fieldname: 'rate',
|
||||
@ -43,7 +44,8 @@ module.exports = {
|
||||
let baseRate = await doc.getFrom('Item', row.item, 'rate');
|
||||
return baseRate / doc.exchangeRate;
|
||||
},
|
||||
getCurrency: (row, doc) => doc.currency
|
||||
getCurrency: (row, doc) => doc.currency,
|
||||
formulaDependsOn: ['item']
|
||||
},
|
||||
{
|
||||
fieldname: 'baseRate',
|
||||
@ -66,10 +68,8 @@ module.exports = {
|
||||
label: 'Tax',
|
||||
fieldtype: 'Link',
|
||||
target: 'Tax',
|
||||
formula: (row, doc) => {
|
||||
if (row.tax) return row.tax;
|
||||
return doc.getFrom('Item', row.item, 'tax');
|
||||
}
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'tax'),
|
||||
formulaDependsOn: ['item']
|
||||
},
|
||||
{
|
||||
fieldname: 'amount',
|
||||
|
@ -54,23 +54,18 @@ export default {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
openFileSelector() {
|
||||
remote.dialog.showOpenDialog(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
title: frappe._('Select Image'),
|
||||
properties: ['openFile'],
|
||||
filters: [
|
||||
{ name: 'Image', extensions: ['png', 'jpg', 'jpeg', 'webp'] }
|
||||
]
|
||||
},
|
||||
async files => {
|
||||
if (files && files[0]) {
|
||||
let dataURL = await this.getDataURL(files[0]);
|
||||
this.triggerChange(dataURL);
|
||||
}
|
||||
}
|
||||
);
|
||||
async openFileSelector() {
|
||||
const options = {
|
||||
title: frappe._('Select Image'),
|
||||
properties: ['openFile'],
|
||||
filters: [{ name: 'Image', extensions: ['png', 'jpg', 'jpeg', 'webp'] }]
|
||||
};
|
||||
|
||||
const { filePaths } = await remote.dialog.showOpenDialog(options);
|
||||
if (filePaths && filePaths[0]) {
|
||||
let dataURL = await this.getDataURL(filePaths[0]);
|
||||
this.triggerChange(dataURL);
|
||||
}
|
||||
},
|
||||
getDataURL(filePath) {
|
||||
let fs = require('fs');
|
||||
|
@ -9,7 +9,6 @@
|
||||
:class="['resize-none', inputClasses]"
|
||||
:value="value"
|
||||
:placeholder="inputPlaceholder"
|
||||
:readonly="isReadOnly"
|
||||
@blur="e => triggerChange(e.target.value)"
|
||||
@focus="e => $emit('focus', e)"
|
||||
@input="e => $emit('input', e)"
|
||||
|
@ -199,6 +199,13 @@ export default {
|
||||
},
|
||||
|
||||
toValue(date) {
|
||||
// toISOString is buggy and reduces the day by one
|
||||
// this is because it considers the UTC timestamp
|
||||
// in order to circumvent that we need to use luxon/moment
|
||||
// but that refactor could take some time, so fixing the time difference
|
||||
// as suggested in this answer.
|
||||
// https://stackoverflow.com/a/16084846/3541205
|
||||
date.setHours(0, -date.getTimezoneOffset(), 0, 0);
|
||||
return date.toISOString().slice(0, 10);
|
||||
},
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="py-4 px-8 flex justify-between window-drag">
|
||||
<div class="my-4 mx-4 flex justify-between window-drag">
|
||||
<div class="window-no-drag">
|
||||
<slot name="title" />
|
||||
</div>
|
||||
|
@ -63,6 +63,10 @@ import router from './router';
|
||||
console.error(err, vm, info);
|
||||
};
|
||||
|
||||
process.on('unhandledRejection', error => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
el: '#app',
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="mt-6">
|
||||
<div class="w-96 mx-auto mt-4">
|
||||
<template v-if="hasData">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="font-medium">{{ _('Cashflow') }}</div>
|
||||
@ -21,7 +21,7 @@
|
||||
v-else
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 889 240"
|
||||
class="w-full h-full"
|
||||
class="w-10/12 h-5/6 ml-4 mr-4 my-2"
|
||||
>
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="100%" x2="50%" y2=".889%" id="a">
|
||||
|
@ -6,17 +6,17 @@
|
||||
<SearchBar class="ml-2" />
|
||||
</template>
|
||||
</PageHeader>
|
||||
<div class="px-8">
|
||||
<div class="mx-4">
|
||||
<div class="border-t" />
|
||||
<Cashflow />
|
||||
<div class="my-10 border-t" />
|
||||
<div class="my-4 mt-0 border-t" />
|
||||
<UnpaidInvoices />
|
||||
<div class="my-10 border-t" />
|
||||
<div class="flex -mx-4">
|
||||
<div class="w-1/2 px-4">
|
||||
<div class="my-4 border-t" />
|
||||
<div class="w-96 flex mx-auto">
|
||||
<div class="w-1/2 mx-4">
|
||||
<ProfitAndLoss />
|
||||
</div>
|
||||
<div class="w-1/2 px-4">
|
||||
<div class="w-1/2 mx-4">
|
||||
<Expenses />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -30,7 +30,7 @@
|
||||
<div
|
||||
v-if="hasData"
|
||||
class="absolute text-base text-center font-semibold"
|
||||
style="right: 3.8rem; top: 32%;"
|
||||
style="right: 6rem; top: 32%;"
|
||||
>
|
||||
<div>
|
||||
{{ frappe.format(totalExpense, 'Currency') }}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="flex -mx-4">
|
||||
<div class="w-96 flex justify-between mx-auto">
|
||||
<div
|
||||
class="w-1/2 px-4 flex flex-col justify-between"
|
||||
class="w-5/12 mx-4 flex flex-col justify-between"
|
||||
v-for="invoice in invoices"
|
||||
:key="invoice.title"
|
||||
>
|
||||
|
@ -22,11 +22,7 @@
|
||||
</Button>
|
||||
</template>
|
||||
</PageHeader>
|
||||
<div
|
||||
v-if="doc"
|
||||
class="flex justify-center flex-1 mb-8 mt-2"
|
||||
:class="doc.submitted && 'pointer-events-none'"
|
||||
>
|
||||
<div v-if="doc" class="flex justify-center flex-1 mb-8 mt-2">
|
||||
<div
|
||||
class="border rounded-lg shadow h-full flex flex-col justify-between"
|
||||
style="width: 600px"
|
||||
@ -45,6 +41,8 @@
|
||||
@change="value => doc.set('entryType', value)"
|
||||
input-class="bg-gray-100 px-3 py-2 text-base"
|
||||
:show-label="true"
|
||||
:read-only="doc.submitted"
|
||||
:class="doc.submitted && 'pointer-events-none'"
|
||||
/>
|
||||
<FormControl
|
||||
class="mt-2"
|
||||
@ -54,6 +52,8 @@
|
||||
@change="value => doc.set('date', value)"
|
||||
input-class="bg-gray-100 px-3 py-2 text-base"
|
||||
:show-label="true"
|
||||
:read-only="doc.submitted"
|
||||
:class="doc.submitted && 'pointer-events-none'"
|
||||
/>
|
||||
</div>
|
||||
<div class="w-1/3">
|
||||
@ -64,6 +64,8 @@
|
||||
@change="value => doc.set('referenceNumber', value)"
|
||||
input-class="bg-gray-100 p-2 text-base"
|
||||
:show-label="true"
|
||||
:read-only="doc.submitted"
|
||||
:class="doc.submitted && 'pointer-events-none'"
|
||||
/>
|
||||
<FormControl
|
||||
class="mt-2"
|
||||
@ -73,6 +75,8 @@
|
||||
@change="value => doc.set('referenceDate', value)"
|
||||
input-class="bg-gray-100 px-3 py-2 text-base"
|
||||
:show-label="true"
|
||||
:read-only="doc.submitted"
|
||||
:class="doc.submitted && 'pointer-events-none'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -82,6 +86,7 @@
|
||||
:df="meta.getField('accounts')"
|
||||
:value="doc.accounts"
|
||||
:showHeader="true"
|
||||
:max-rows-before-overflow="4"
|
||||
@change="value => doc.set('accounts', value)"
|
||||
:read-only="doc.submitted"
|
||||
/>
|
||||
@ -97,6 +102,8 @@
|
||||
:df="meta.getField('userRemark')"
|
||||
:value="doc.userRemark"
|
||||
@change="value => doc.set('userRemark', value)"
|
||||
:class="doc.submitted && 'pointer-events-none'"
|
||||
:read-only="doc.submitted"
|
||||
/>
|
||||
</div>
|
||||
<div class="text-right font-semibold text-green-700 px-3">
|
||||
|
@ -90,24 +90,20 @@ export default {
|
||||
let html = this.$refs.printContainer.innerHTML;
|
||||
makePDF(html, destination);
|
||||
},
|
||||
getSavePath() {
|
||||
return new Promise(resolve => {
|
||||
remote.dialog.showSaveDialog(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
title: this._('Select folder'),
|
||||
defaultPath: `${this.name}.pdf`
|
||||
},
|
||||
filePath => {
|
||||
if (filePath) {
|
||||
if (!filePath.endsWith('.pdf')) {
|
||||
filePath = filePath + '.pdf';
|
||||
}
|
||||
resolve(filePath);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
async getSavePath() {
|
||||
const options = {
|
||||
title: this._('Select folder'),
|
||||
defaultPath: `${this.name}.pdf`
|
||||
};
|
||||
|
||||
let { filePath } = await remote.dialog.showSaveDialog(options);
|
||||
if (filePath) {
|
||||
if (!filePath.endsWith('.pdf')) {
|
||||
filePath = filePath + '.pdf';
|
||||
}
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
154
src/utils.js
154
src/utils.js
@ -9,59 +9,50 @@ import router from '@/router';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import config from '@/config';
|
||||
|
||||
export function createNewDatabase() {
|
||||
return new Promise(resolve => {
|
||||
remote.dialog.showSaveDialog(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
title: _('Select folder'),
|
||||
defaultPath: 'frappe-books.db'
|
||||
},
|
||||
filePath => {
|
||||
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);
|
||||
resolve(filePath);
|
||||
}
|
||||
},
|
||||
{ label: _('Cancel'), action() {} }
|
||||
]
|
||||
});
|
||||
} else {
|
||||
resolve(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function loadExistingDatabase() {
|
||||
return new Promise(resolve => {
|
||||
remote.dialog.showOpenDialog(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
title: _('Select file'),
|
||||
properties: ['openFile'],
|
||||
filters: [{ name: 'SQLite DB File', extensions: ['db'] }]
|
||||
},
|
||||
files => {
|
||||
if (files && files[0]) {
|
||||
resolve(files[0]);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
export async function loadExistingDatabase() {
|
||||
const options = {
|
||||
title: _('Select file'),
|
||||
properties: ['openFile'],
|
||||
filters: [{ name: 'SQLite DB File', extensions: ['db'] }]
|
||||
};
|
||||
|
||||
let { filePaths } = await remote.dialog.showOpenDialog(options);
|
||||
|
||||
if (filePaths && filePaths[0]) {
|
||||
return filePaths[0]
|
||||
}
|
||||
}
|
||||
|
||||
export async function connectToLocalDatabase(filepath) {
|
||||
@ -90,22 +81,25 @@ export async function connectToLocalDatabase(filepath) {
|
||||
config.set('lastSelectedFilePath', filepath);
|
||||
}
|
||||
|
||||
export function showMessageDialog({ message, description, buttons = [] }) {
|
||||
export async function showMessageDialog({
|
||||
message,
|
||||
description,
|
||||
buttons = []
|
||||
}) {
|
||||
let buttonLabels = buttons.map(a => a.label);
|
||||
remote.dialog.showMessageBox(
|
||||
const { response } = await remote.dialog.showMessageBox(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
message,
|
||||
detail: description,
|
||||
buttons: buttonLabels
|
||||
},
|
||||
response => {
|
||||
let button = buttons[response];
|
||||
if (button && button.action) {
|
||||
button.action();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let button = buttons[response];
|
||||
if (button && button.action) {
|
||||
button.action();
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteDocWithPrompt(doc) {
|
||||
@ -203,6 +197,16 @@ export function handleErrorWithDialog(e, doc) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
const styles = document.getElementsByTagName('style');
|
||||
|
||||
for (let style of styles) {
|
||||
contents.insertCSS(style.innerHTML);
|
||||
}
|
||||
}
|
||||
|
||||
export function makePDF(html, destination) {
|
||||
const { BrowserWindow } = remote;
|
||||
|
||||
@ -211,7 +215,8 @@ export function makePDF(html, destination) {
|
||||
height: 842,
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION
|
||||
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
|
||||
enableRemoteModule: true
|
||||
}
|
||||
});
|
||||
|
||||
@ -234,23 +239,24 @@ export function makePDF(html, destination) {
|
||||
|
||||
printWindow.webContents.executeJavaScript(code);
|
||||
|
||||
const printOptions = {
|
||||
marginsType: 1, // no margin
|
||||
pageSize: 'A4',
|
||||
printBackground: true,
|
||||
printBackgrounds: true,
|
||||
printSelectionOnly: false
|
||||
};
|
||||
|
||||
return new Promise(resolve => {
|
||||
printWindow.webContents.on('did-finish-load', () => {
|
||||
printWindow.webContents.printToPDF(
|
||||
{
|
||||
marginsType: 1, // no margin
|
||||
pageSize: 'A4',
|
||||
printBackground: true
|
||||
},
|
||||
(error, data) => {
|
||||
injectCSS(printWindow.webContents);
|
||||
printWindow.webContents.printToPDF(printOptions).then(data => {
|
||||
printWindow.close();
|
||||
fs.writeFile(destination, data, error => {
|
||||
if (error) throw error;
|
||||
printWindow.close();
|
||||
fs.writeFile(destination, data, error => {
|
||||
if (error) throw error;
|
||||
resolve(shell.openItem(destination));
|
||||
});
|
||||
}
|
||||
);
|
||||
resolve(shell.openItem(destination));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user