mirror of
https://github.com/frappe/books.git
synced 2025-02-08 15:08:29 +00:00
incr: refactor and type src/utils
This commit is contained in:
parent
ae6a5e52f2
commit
c56850d08f
@ -1,6 +1,6 @@
|
|||||||
import { showMessageDialog } from '@/utils';
|
|
||||||
import frappe, { t } from 'fyo';
|
import frappe, { t } from 'fyo';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
|
import { showMessageDialog } from 'src/utils';
|
||||||
import { stateCodeMap } from '../regional/in';
|
import { stateCodeMap } from '../regional/in';
|
||||||
import { exportCsv, saveExportData } from '../reports/commonExporter';
|
import { exportCsv, saveExportData } from '../reports/commonExporter';
|
||||||
import { getSavePath } from '../src/utils';
|
import { getSavePath } from '../src/utils';
|
||||||
@ -50,7 +50,7 @@ export async function generateGstr1Json(getReportData) {
|
|||||||
if (!gstin) {
|
if (!gstin) {
|
||||||
showMessageDialog({
|
showMessageDialog({
|
||||||
message: t`Export Failed`,
|
message: t`Export Failed`,
|
||||||
description: t`Please set GSTIN in General Settings.`,
|
detail: t`Please set GSTIN in General Settings.`,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -232,7 +232,7 @@ export async function generateGstr2Csv(getReportData) {
|
|||||||
if (!gstin) {
|
if (!gstin) {
|
||||||
showMessageDialog({
|
showMessageDialog({
|
||||||
message: t`Export Failed`,
|
message: t`Export Failed`,
|
||||||
description: t`Please set GSTIN in General Settings.`,
|
detail: t`Please set GSTIN in General Settings.`,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -313,7 +313,7 @@ export async function generateGstr1Csv(getReportData) {
|
|||||||
if (!gstin) {
|
if (!gstin) {
|
||||||
showMessageDialog({
|
showMessageDialog({
|
||||||
message: t`Export Failed`,
|
message: t`Export Failed`,
|
||||||
description: t`Please set GSTIN in General Settings.`,
|
detail: t`Please set GSTIN in General Settings.`,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -59,8 +59,11 @@ export type ListsMap = Record<string, ListFunction>;
|
|||||||
|
|
||||||
export interface Action {
|
export interface Action {
|
||||||
label: string;
|
label: string;
|
||||||
condition: (doc: Doc) => boolean;
|
action: (doc: Doc, router: Router) => Promise<void> | void;
|
||||||
action: (doc: Doc, router: Router) => Promise<void>;
|
condition?: (doc: Doc) => boolean;
|
||||||
|
component?: {
|
||||||
|
template?: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ColumnConfig {
|
export interface ColumnConfig {
|
||||||
|
@ -41,7 +41,7 @@ describe('Fyo Init', function () {
|
|||||||
describe('Fyo Docs', function () {
|
describe('Fyo Docs', function () {
|
||||||
const countryCode = 'in';
|
const countryCode = 'in';
|
||||||
let fyo: Fyo;
|
let fyo: Fyo;
|
||||||
const schemas = getSchemas(countryCode);
|
const schemaMap = getSchemas(countryCode);
|
||||||
this.beforeEach(async function () {
|
this.beforeEach(async function () {
|
||||||
fyo = new Fyo({
|
fyo = new Fyo({
|
||||||
DatabaseDemux: DatabaseManager,
|
DatabaseDemux: DatabaseManager,
|
||||||
@ -58,7 +58,14 @@ describe('Fyo Docs', function () {
|
|||||||
await fyo.close();
|
await fyo.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
specify('temp', async function () {
|
specify('getEmptyDoc', async function () {
|
||||||
fyo.db.schemaMap;
|
for (const schemaName in schemaMap) {
|
||||||
|
const schema = schemaMap[schemaName];
|
||||||
|
if (schema?.isSingle) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const doc = fyo.doc.getEmptyDoc(schemaName);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import { Fyo } from 'fyo';
|
||||||
|
import Doc from 'fyo/model/doc';
|
||||||
|
import { Action } from 'fyo/model/types';
|
||||||
import { pesa } from 'pesa';
|
import { pesa } from 'pesa';
|
||||||
|
|
||||||
export function slug(str: string) {
|
export function slug(str: string) {
|
||||||
@ -50,3 +53,12 @@ export function getDuplicates(array: unknown[]) {
|
|||||||
export function isPesa(value: unknown): boolean {
|
export function isPesa(value: unknown): boolean {
|
||||||
return value instanceof pesa().constructor;
|
return value instanceof pesa().constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getActions(doc: Doc, fyo: Fyo): Action[] {
|
||||||
|
const Model = fyo.models[doc.schemaName];
|
||||||
|
if (Model === undefined) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Model.getActions(fyo);
|
||||||
|
}
|
||||||
|
@ -90,6 +90,8 @@ export function t(...args: TranslationLiteral[]): string {
|
|||||||
return new TranslationString(...args).s;
|
return new TranslationString(...args).s;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setLanguageMapOnTranslationString(languageMap: LanguageMap) {
|
export function setLanguageMapOnTranslationString(
|
||||||
|
languageMap: LanguageMap | undefined
|
||||||
|
) {
|
||||||
TranslationString.prototype.languageMap = languageMap;
|
TranslationString.prototype.languageMap = languageMap;
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,12 @@ export abstract class Invoice extends Doc {
|
|||||||
currency?: string;
|
currency?: string;
|
||||||
netTotal?: Money;
|
netTotal?: Money;
|
||||||
baseGrandTotal?: Money;
|
baseGrandTotal?: Money;
|
||||||
|
outstandingAmount?: Money;
|
||||||
exchangeRate?: number;
|
exchangeRate?: number;
|
||||||
|
|
||||||
|
submitted?: boolean;
|
||||||
|
cancelled?: boolean;
|
||||||
|
|
||||||
abstract getPosting(): Promise<LedgerPosting>;
|
abstract getPosting(): Promise<LedgerPosting>;
|
||||||
|
|
||||||
get isSales() {
|
get isSales() {
|
||||||
|
@ -43,7 +43,7 @@ export function getTransactionActions(schemaName: string, fyo: Fyo): Action[] {
|
|||||||
const paymentType = isSales ? 'Receive' : 'Pay';
|
const paymentType = isSales ? 'Receive' : 'Pay';
|
||||||
const hideAccountField = isSales ? 'account' : 'paymentAccount';
|
const hideAccountField = isSales ? 'account' : 'paymentAccount';
|
||||||
|
|
||||||
const { openQuickEdit } = await import('../src/utils');
|
const { openQuickEdit } = await import('src/utils/ui');
|
||||||
await openQuickEdit({
|
await openQuickEdit({
|
||||||
schemaName: 'Payment',
|
schemaName: 'Payment',
|
||||||
name: payment.name as string,
|
name: payment.name as string,
|
||||||
|
@ -1,7 +1,33 @@
|
|||||||
import { partyWithAvatar } from '@/utils';
|
|
||||||
import { t } from 'fyo';
|
import { t } from 'fyo';
|
||||||
|
import Avatar from 'src/components/Avatar.vue';
|
||||||
|
import { fyo } from 'src/initFyo';
|
||||||
import getCommonExportActions from '../commonExporter';
|
import getCommonExportActions from '../commonExporter';
|
||||||
|
|
||||||
|
export function getPartyWithAvatar(partyName) {
|
||||||
|
return {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
imageURL: null,
|
||||||
|
label: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Avatar,
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
const p = await fyo.db.get('Party', partyName);
|
||||||
|
this.imageURL = p.image;
|
||||||
|
this.label = partyName;
|
||||||
|
},
|
||||||
|
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>
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let title = t`General Ledger`;
|
let title = t`General Ledger`;
|
||||||
|
|
||||||
const viewConfig = {
|
const viewConfig = {
|
||||||
@ -117,7 +143,7 @@ const viewConfig = {
|
|||||||
fieldtype: 'Link',
|
fieldtype: 'Link',
|
||||||
fieldname: 'party',
|
fieldname: 'party',
|
||||||
component(cellValue) {
|
component(cellValue) {
|
||||||
return partyWithAvatar(cellValue);
|
return getPartyWithAvatar(cellValue);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
12
src/App.vue
12
src/App.vue
@ -30,16 +30,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import WindowsTitleBar from '@/components/WindowsTitleBar';
|
import { ipcRenderer } from 'electron';
|
||||||
import config from '@/config';
|
import fs from 'fs/promises';
|
||||||
|
import frappe from 'fyo';
|
||||||
|
import WindowsTitleBar from 'src/components/WindowsTitleBar';
|
||||||
|
import config from 'src/config';
|
||||||
import {
|
import {
|
||||||
connectToLocalDatabase,
|
connectToLocalDatabase,
|
||||||
postSetup,
|
postSetup,
|
||||||
purgeCache
|
purgeCache
|
||||||
} from '@/initialization';
|
} from 'src/initialization';
|
||||||
import { ipcRenderer } from 'electron';
|
|
||||||
import fs from 'fs/promises';
|
|
||||||
import frappe from 'fyo';
|
|
||||||
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
|
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
|
||||||
import TelemetryModal from './components/once/TelemetryModal.vue';
|
import TelemetryModal from './components/once/TelemetryModal.vue';
|
||||||
import { showErrorDialog } from './errorHandling';
|
import { showErrorDialog } from './errorHandling';
|
||||||
|
@ -32,9 +32,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Dropdown from 'src/components/Dropdown';
|
||||||
|
import { fuzzyMatch } from 'src/utils';
|
||||||
import Base from './Base';
|
import Base from './Base';
|
||||||
import Dropdown from '@/components/Dropdown';
|
|
||||||
import { fuzzyMatch } from '@/utils';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AutoComplete',
|
name: 'AutoComplete',
|
||||||
|
@ -55,9 +55,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Popover from 'src/components/Popover';
|
||||||
|
import Row from 'src/components/Row';
|
||||||
import Base from './Base';
|
import Base from './Base';
|
||||||
import Row from '@/components/Row';
|
|
||||||
import Popover from '@/components/Popover';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Color',
|
name: 'Color',
|
||||||
|
@ -7,10 +7,10 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import config from '@/config';
|
|
||||||
import { languageCodeMap } from '@/languageCodeMap';
|
|
||||||
import { setLanguageMap } from '@/utils';
|
|
||||||
import { DEFAULT_LANGUAGE } from 'frappe/utils/consts';
|
import { DEFAULT_LANGUAGE } from 'frappe/utils/consts';
|
||||||
|
import config from 'src/config';
|
||||||
|
import { languageCodeMap } from 'src/languageCodeMap';
|
||||||
|
import { setLanguageMap } from 'src/utils';
|
||||||
import FormControl from './FormControl';
|
import FormControl from './FormControl';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import Badge from '@/components/Badge';
|
import Badge from 'src/components/Badge';
|
||||||
import { openQuickEdit } from '@/utils';
|
import { openQuickEdit } from 'src/utils';
|
||||||
import frappe, { t } from 'frappe';
|
import frappe, { t } from 'frappe';
|
||||||
import { markRaw } from 'vue';
|
import { markRaw } from 'vue';
|
||||||
import AutoComplete from './AutoComplete';
|
import AutoComplete from './AutoComplete';
|
||||||
<script>
|
<script>
|
||||||
import Badge from '@/components/Badge';
|
|
||||||
import { openQuickEdit } from '@/utils';
|
|
||||||
import frappe, { t } from 'frappe';
|
import frappe, { t } from 'frappe';
|
||||||
|
import Badge from 'src/components/Badge';
|
||||||
|
import { openQuickEdit } from 'src/utils';
|
||||||
import { markRaw } from 'vue';
|
import { markRaw } from 'vue';
|
||||||
import AutoComplete from './AutoComplete';
|
import AutoComplete from './AutoComplete';
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
import Row from '@/components/Row';
|
import Row from 'src/components/Row';
|
||||||
import Base from './Base';
|
import Base from './Base';
|
||||||
import TableRow from './TableRow';
|
import TableRow from './TableRow';
|
||||||
|
|
||||||
|
@ -32,9 +32,9 @@
|
|||||||
</Row>
|
</Row>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import FormControl from './FormControl';
|
import Row from 'src/components/Row';
|
||||||
import Row from '@/components/Row';
|
|
||||||
import { getErrorMessage } from '../../errorHandling';
|
import { getErrorMessage } from '../../errorHandling';
|
||||||
|
import FormControl from './FormControl';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TableRow',
|
name: 'TableRow',
|
||||||
|
@ -21,8 +21,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Dropdown from '@/components/Dropdown';
|
import Button from 'src/components/Button';
|
||||||
import Button from '@/components/Button';
|
import Dropdown from 'src/components/Dropdown';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DropdownWithActions',
|
name: 'DropdownWithActions',
|
||||||
|
@ -47,10 +47,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import InvoiceTemplate1 from '@/../models/doctype/SalesInvoice/Templates/InvoiceTemplate1';
|
import InvoiceTemplate1 from 'src/../models/doctype/SalesInvoice/Templates/InvoiceTemplate1';
|
||||||
import InvoiceTemplate2 from '@/../models/doctype/SalesInvoice/Templates/InvoiceTemplate2';
|
import InvoiceTemplate2 from 'src/../models/doctype/SalesInvoice/Templates/InvoiceTemplate2';
|
||||||
import InvoiceTemplate3 from '@/../models/doctype/SalesInvoice/Templates/InvoiceTemplate3';
|
import InvoiceTemplate3 from 'src/../models/doctype/SalesInvoice/Templates/InvoiceTemplate3';
|
||||||
import InvoiceCustomizer from '@/components/InvoiceCustomizer';
|
import InvoiceCustomizer from 'src/components/InvoiceCustomizer';
|
||||||
|
|
||||||
const invoiceTemplates = {
|
const invoiceTemplates = {
|
||||||
'Basic I': InvoiceTemplate1,
|
'Basic I': InvoiceTemplate1,
|
||||||
|
@ -41,9 +41,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import Dropdown from '@/components/Dropdown';
|
|
||||||
import { routeTo } from '@/utils';
|
|
||||||
import frappe, { t } from 'frappe';
|
import frappe, { t } from 'frappe';
|
||||||
|
import Dropdown from 'src/components/Dropdown';
|
||||||
|
import { routeTo } from 'src/utils';
|
||||||
import reports from '../../reports/view';
|
import reports from '../../reports/view';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -96,10 +96,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import Button from '@/components/Button';
|
|
||||||
import { reportIssue } from '@/errorHandling';
|
|
||||||
import { routeTo } from '@/utils';
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import Button from 'src/components/Button';
|
||||||
|
import { reportIssue } from 'src/errorHandling';
|
||||||
|
import { routeTo } from 'src/utils';
|
||||||
import router from '../router';
|
import router from '../router';
|
||||||
import sidebarConfig from '../sidebarConfig';
|
import sidebarConfig from '../sidebarConfig';
|
||||||
import Icon from './Icon.vue';
|
import Icon from './Icon.vue';
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
}}</Badge>
|
}}</Badge>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import Badge from 'src/components/Badge';
|
||||||
import { statusColor } from '../colors';
|
import { statusColor } from '../colors';
|
||||||
import Badge from '@/components/Badge';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'StatusBadge',
|
name: 'StatusBadge',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import Button from '@/components/Button';
|
import Button from 'src/components/Button';
|
||||||
import FormControl from '@/components/Controls/FormControl';
|
import FormControl from 'src/components/Controls/FormControl';
|
||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
import { getErrorMessage, handleErrorWithDialog } from '../errorHandling';
|
import { getErrorMessage, handleErrorWithDialog } from '../errorHandling';
|
||||||
<template>
|
<template>
|
||||||
@ -90,9 +90,9 @@ import { getErrorMessage, handleErrorWithDialog } from '../errorHandling';
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import Button from '@/components/Button';
|
|
||||||
import FormControl from '@/components/Controls/FormControl';
|
|
||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
|
import Button from 'src/components/Button';
|
||||||
|
import FormControl from 'src/components/Controls/FormControl';
|
||||||
import { getErrorMessage, handleErrorWithDialog } from '../errorHandling';
|
import { getErrorMessage, handleErrorWithDialog } from '../errorHandling';
|
||||||
|
|
||||||
let TwoColumnForm = {
|
let TwoColumnForm = {
|
||||||
|
@ -49,8 +49,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { routeTo } from '@/utils';
|
|
||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
|
import { routeTo } from 'src/utils';
|
||||||
import { getStatusColumn } from '../Transaction/Transaction';
|
import { getStatusColumn } from '../Transaction/Transaction';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { runWindowAction } from '@/utils';
|
import { runWindowAction } from 'src/utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WindowControls',
|
name: 'WindowControls',
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import { runWindowAction } from '@/utils';
|
import { runWindowAction } from 'src/utils';
|
||||||
import { IPC_MESSAGES } from 'utils/messages';
|
import { IPC_MESSAGES } from 'utils/messages';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -49,10 +49,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import config, { ConfigKeys, TelemetrySetting } from '@/config';
|
import config, { ConfigKeys, TelemetrySetting } from 'src/config';
|
||||||
import { getTelemetryOptions } from '@/telemetry/helpers';
|
import { getTelemetryOptions } from 'src/telemetry/helpers';
|
||||||
import telemetry from '@/telemetry/telemetry';
|
import telemetry from 'src/telemetry/telemetry';
|
||||||
import { NounEnum, Verb } from '@/telemetry/types';
|
import { NounEnum, Verb } from 'src/telemetry/types';
|
||||||
import Button from '../Button.vue';
|
import Button from '../Button.vue';
|
||||||
import FormControl from '../Controls/FormControl';
|
import FormControl from '../Controls/FormControl';
|
||||||
import FeatherIcon from '../FeatherIcon.vue';
|
import FeatherIcon from '../FeatherIcon.vue';
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import frappe from 'fyo';
|
import { t } from 'fyo';
|
||||||
import { DocValueMap } from 'fyo/core/types';
|
import { DocValueMap } from 'fyo/core/types';
|
||||||
import Doc from 'fyo/model/doc';
|
import Doc from 'fyo/model/doc';
|
||||||
import { isNameAutoSet } from 'fyo/model/naming';
|
import { isNameAutoSet } from 'fyo/model/naming';
|
||||||
|
import { Noun, Verb } from 'fyo/telemetry/types';
|
||||||
import { FieldType, FieldTypeEnum } from 'schemas/types';
|
import { FieldType, FieldTypeEnum } from 'schemas/types';
|
||||||
import telemetry from '../frappe/telemetry/telemetry';
|
|
||||||
import { Noun, Verb } from '../frappe/telemetry/types';
|
|
||||||
import { parseCSV } from '../utils/csvParser';
|
import { parseCSV } from '../utils/csvParser';
|
||||||
|
import { fyo } from './initFyo';
|
||||||
|
|
||||||
export const importable = [
|
export const importable = [
|
||||||
'SalesInvoice',
|
'SalesInvoice',
|
||||||
@ -364,7 +364,7 @@ export class Importer {
|
|||||||
|
|
||||||
async importData(setLoadingStatus: LoadingStatusCallback): Promise<Status> {
|
async importData(setLoadingStatus: LoadingStatusCallback): Promise<Status> {
|
||||||
const status: Status = { success: false, names: [], message: '' };
|
const status: Status = { success: false, names: [], message: '' };
|
||||||
const shouldDeleteName = isNameAutoSet(this.doctype);
|
const shouldDeleteName = isNameAutoSet(this.doctype, fyo);
|
||||||
const docObjs = this.getDocs();
|
const docObjs = this.getDocs();
|
||||||
|
|
||||||
let entriesMade = 0;
|
let entriesMade = 0;
|
||||||
@ -383,7 +383,7 @@ export class Importer {
|
|||||||
delete docObj[key];
|
delete docObj[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
const doc: Doc = frappe.doc.getEmptyDoc(this.doctype, false);
|
const doc: Doc = fyo.doc.getEmptyDoc(this.doctype, false);
|
||||||
try {
|
try {
|
||||||
await this.makeEntry(doc, docObj);
|
await this.makeEntry(doc, docObj);
|
||||||
entriesMade += 1;
|
entriesMade += 1;
|
||||||
@ -391,7 +391,7 @@ export class Importer {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
setLoadingStatus(false, entriesMade, docObjs.length);
|
setLoadingStatus(false, entriesMade, docObjs.length);
|
||||||
|
|
||||||
telemetry.log(Verb.Imported, this.doctype as Noun, {
|
fyo.telemetry.log(Verb.Imported, this.doctype as Noun, {
|
||||||
success: false,
|
success: false,
|
||||||
count: entriesMade,
|
count: entriesMade,
|
||||||
});
|
});
|
||||||
@ -405,7 +405,7 @@ export class Importer {
|
|||||||
setLoadingStatus(false, entriesMade, docObjs.length);
|
setLoadingStatus(false, entriesMade, docObjs.length);
|
||||||
status.success = true;
|
status.success = true;
|
||||||
|
|
||||||
telemetry.log(Verb.Imported, this.doctype as Noun, {
|
fyo.telemetry.log(Verb.Imported, this.doctype as Noun, {
|
||||||
success: true,
|
success: true,
|
||||||
count: entriesMade,
|
count: entriesMade,
|
||||||
});
|
});
|
||||||
@ -426,18 +426,18 @@ export class Importer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleError(doc: Doc, err: Error, status: Status): Status {
|
handleError(doc: Doc, err: Error, status: Status): Status {
|
||||||
const messages = [frappe.t`Could not import ${this.doctype} ${doc.name!}.`];
|
const messages = [t`Could not import ${this.doctype} ${doc.name!}.`];
|
||||||
|
|
||||||
const message = err.message;
|
const message = err.message;
|
||||||
if (message?.includes('UNIQUE constraint failed')) {
|
if (message?.includes('UNIQUE constraint failed')) {
|
||||||
messages.push(frappe.t`${doc.name!} already exists.`);
|
messages.push(t`${doc.name!} already exists.`);
|
||||||
} else if (message) {
|
} else if (message) {
|
||||||
messages.push(message);
|
messages.push(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.names.length) {
|
if (status.names.length) {
|
||||||
messages.push(
|
messages.push(
|
||||||
frappe.t`The following ${
|
t`The following ${
|
||||||
status.names.length
|
status.names.length
|
||||||
} entries were created: ${status.names.join(', ')}`
|
} entries were created: ${status.names.join(', ')}`
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import frappe, { t } from 'fyo';
|
import { t } from 'fyo';
|
||||||
|
import { ConfigKeys } from 'fyo/core/types';
|
||||||
import Doc from 'fyo/model/doc';
|
import Doc from 'fyo/model/doc';
|
||||||
|
import { TelemetrySetting } from 'fyo/telemetry/types';
|
||||||
import {
|
import {
|
||||||
DuplicateEntryError,
|
DuplicateEntryError,
|
||||||
LinkValidationError,
|
LinkValidationError,
|
||||||
@ -9,12 +11,12 @@ import {
|
|||||||
} from 'fyo/utils/errors';
|
} from 'fyo/utils/errors';
|
||||||
import { ErrorLog } from 'fyo/utils/types';
|
import { ErrorLog } from 'fyo/utils/types';
|
||||||
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
|
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
|
||||||
import telemetry from '../frappe/telemetry/telemetry';
|
import { fyo } from './initFyo';
|
||||||
import config, { ConfigKeys, TelemetrySetting } from '../utils/config';
|
import { ToastOptions } from './utils/types';
|
||||||
import { showMessageDialog, showToast } from './utils.js';
|
import { showMessageDialog, showToast } from './utils/ui';
|
||||||
|
|
||||||
function getCanLog(): boolean {
|
function getCanLog(): boolean {
|
||||||
const telemetrySetting = config.get(ConfigKeys.Telemetry);
|
const telemetrySetting = fyo.config.get(ConfigKeys.Telemetry);
|
||||||
return telemetrySetting !== TelemetrySetting.dontLogAnything;
|
return telemetrySetting !== TelemetrySetting.dontLogAnything;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +38,7 @@ async function reportError(errorLogObj: ErrorLog, cb?: Function) {
|
|||||||
more: JSON.stringify(errorLogObj.more ?? {}),
|
more: JSON.stringify(errorLogObj.more ?? {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (frappe.store.isDevelopment) {
|
if (fyo.store.isDevelopment) {
|
||||||
console.log('errorHandling');
|
console.log('errorHandling');
|
||||||
console.log(body);
|
console.log(body);
|
||||||
}
|
}
|
||||||
@ -46,7 +48,7 @@ async function reportError(errorLogObj: ErrorLog, cb?: Function) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getToastProps(errorLogObj: ErrorLog, canLog: boolean, cb?: Function) {
|
function getToastProps(errorLogObj: ErrorLog, canLog: boolean, cb?: Function) {
|
||||||
const props = {
|
const props: ToastOptions = {
|
||||||
message: t`Error: ` + errorLogObj.name,
|
message: t`Error: ` + errorLogObj.name,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
};
|
};
|
||||||
@ -73,7 +75,7 @@ export function getErrorLogObject(
|
|||||||
const errorLogObj = { name, stack, message, more };
|
const errorLogObj = { name, stack, message, more };
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
frappe.errorLog.push(errorLogObj);
|
fyo.errorLog.push(errorLogObj);
|
||||||
|
|
||||||
return errorLogObj;
|
return errorLogObj;
|
||||||
}
|
}
|
||||||
@ -84,7 +86,7 @@ export function handleError(
|
|||||||
more?: Record<string, unknown>,
|
more?: Record<string, unknown>,
|
||||||
cb?: Function
|
cb?: Function
|
||||||
) {
|
) {
|
||||||
telemetry.error(error.name);
|
fyo.telemetry.error(error.name);
|
||||||
if (shouldLog) {
|
if (shouldLog) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
@ -124,7 +126,7 @@ export function handleErrorWithDialog(error: Error, doc?: Doc) {
|
|||||||
const errorMessage = getErrorMessage(error, doc);
|
const errorMessage = getErrorMessage(error, doc);
|
||||||
handleError(false, error, { errorMessage, doc });
|
handleError(false, error, { errorMessage, doc });
|
||||||
|
|
||||||
showMessageDialog({ message: error.name, description: errorMessage });
|
showMessageDialog({ message: error.name, detail: errorMessage });
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
// Language: Language Code in books/translations
|
|
||||||
export const languageCodeMap = {
|
|
||||||
English: 'en',
|
|
||||||
French: 'fr',
|
|
||||||
German: 'de',
|
|
||||||
Portuguese: 'pt',
|
|
||||||
Arabic: 'ar',
|
|
||||||
Catalan: 'ca-ES',
|
|
||||||
};
|
|
19
src/main.js
19
src/main.js
@ -1,10 +1,10 @@
|
|||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import frappe from 'frappe';
|
|
||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import FeatherIcon from './components/FeatherIcon';
|
import FeatherIcon from './components/FeatherIcon';
|
||||||
import config, { ConfigKeys } from './config';
|
import config, { ConfigKeys } from './config';
|
||||||
import { getErrorHandled, handleError } from './errorHandling';
|
import { getErrorHandled, handleError } from './errorHandling';
|
||||||
|
import { fyo } from './initFyo';
|
||||||
import { IPC_ACTIONS } from './messages';
|
import { IPC_ACTIONS } from './messages';
|
||||||
import { incrementOpenCount } from './renderer/helpers';
|
import { incrementOpenCount } from './renderer/helpers';
|
||||||
import registerIpcRendererListeners from './renderer/registerIpcRendererListeners';
|
import registerIpcRendererListeners from './renderer/registerIpcRendererListeners';
|
||||||
@ -13,7 +13,7 @@ import { outsideClickDirective } from './ui';
|
|||||||
import { setLanguageMap, stringifyCircular } from './utils';
|
import { setLanguageMap, stringifyCircular } from './utils';
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const language = config.get(ConfigKeys.Language);
|
const language = fyo.config.get(ConfigKeys.Language);
|
||||||
if (language) {
|
if (language) {
|
||||||
await setLanguageMap(language);
|
await setLanguageMap(language);
|
||||||
}
|
}
|
||||||
@ -22,15 +22,15 @@ import { setLanguageMap, stringifyCircular } from './utils';
|
|||||||
window.config = config;
|
window.config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
frappe.isElectron = true;
|
fyo.isElectron = true;
|
||||||
|
|
||||||
const models = (await import('../models')).default;
|
const models = (await import('../models')).default;
|
||||||
await frappe.initializeAndRegister(models);
|
await fyo.initializeAndRegister(models);
|
||||||
|
|
||||||
ipcRenderer.send = getErrorHandled(ipcRenderer.send);
|
ipcRenderer.send = getErrorHandled(ipcRenderer.send);
|
||||||
ipcRenderer.invoke = getErrorHandled(ipcRenderer.invoke);
|
ipcRenderer.invoke = getErrorHandled(ipcRenderer.invoke);
|
||||||
|
|
||||||
window.frappe = frappe;
|
window.frappe = fyo;
|
||||||
|
|
||||||
window.onerror = (message, source, lineno, colno, error) => {
|
window.onerror = (message, source, lineno, colno, error) => {
|
||||||
error = error ?? new Error('triggered in window.onerror');
|
error = error ?? new Error('triggered in window.onerror');
|
||||||
@ -50,7 +50,7 @@ import { setLanguageMap, stringifyCircular } from './utils';
|
|||||||
app.mixin({
|
app.mixin({
|
||||||
computed: {
|
computed: {
|
||||||
frappe() {
|
frappe() {
|
||||||
return frappe;
|
return fyo;
|
||||||
},
|
},
|
||||||
platform() {
|
platform() {
|
||||||
switch (process.platform) {
|
switch (process.platform) {
|
||||||
@ -66,8 +66,8 @@ import { setLanguageMap, stringifyCircular } from './utils';
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
t: frappe.t,
|
t: fyo.t,
|
||||||
T: frappe.T,
|
T: fyo.T,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ import { setLanguageMap, stringifyCircular } from './utils';
|
|||||||
console.error(err, vm, info);
|
console.error(err, vm, info);
|
||||||
};
|
};
|
||||||
|
|
||||||
frappe.store.appVersion = await ipcRenderer.invoke(IPC_ACTIONS.GET_VERSION);
|
fyo.store.appVersion = await ipcRenderer.invoke(IPC_ACTIONS.GET_VERSION);
|
||||||
incrementOpenCount();
|
incrementOpenCount();
|
||||||
app.mount('body');
|
app.mount('body');
|
||||||
|
|
||||||
@ -100,4 +100,3 @@ import { setLanguageMap, stringifyCircular } from './utils';
|
|||||||
handleError(true, error, {}, () => process.exit(1));
|
handleError(true, error, {}, () => process.exit(1));
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import PageHeader from '@/components/PageHeader';
|
import PageHeader from 'src/components/PageHeader';
|
||||||
import SearchBar from '@/components/SearchBar';
|
import SearchBar from 'src/components/SearchBar';
|
||||||
import { openQuickEdit } from '@/utils';
|
import { openQuickEdit } from 'src/utils';
|
||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
import { nextTick } from 'vue';
|
import { nextTick } from 'vue';
|
||||||
import { handleErrorWithDialog } from '../errorHandling';
|
import { handleErrorWithDialog } from '../errorHandling';
|
||||||
@ -135,10 +135,10 @@ import { handleErrorWithDialog } from '../errorHandling';
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import PageHeader from '@/components/PageHeader';
|
|
||||||
import SearchBar from '@/components/SearchBar';
|
|
||||||
import { openQuickEdit } from '@/utils';
|
|
||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
|
import PageHeader from 'src/components/PageHeader';
|
||||||
|
import SearchBar from 'src/components/SearchBar';
|
||||||
|
import { openQuickEdit } from 'src/utils';
|
||||||
import { nextTick } from 'vue';
|
import { nextTick } from 'vue';
|
||||||
import { handleErrorWithDialog } from '../errorHandling';
|
import { handleErrorWithDialog } from '../errorHandling';
|
||||||
|
|
||||||
|
@ -101,12 +101,12 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
import PeriodSelector from './PeriodSelector';
|
import { getYMax } from 'src/components/Charts/chartUtils';
|
||||||
|
import LineChart from 'src/components/Charts/LineChart.vue';
|
||||||
|
import { formatXLabels } from 'src/utils';
|
||||||
import Cashflow from '../../../reports/Cashflow/Cashflow';
|
import Cashflow from '../../../reports/Cashflow/Cashflow';
|
||||||
import { getDatesAndPeriodicity } from './getDatesAndPeriodicity';
|
import { getDatesAndPeriodicity } from './getDatesAndPeriodicity';
|
||||||
import LineChart from '@/components/Charts/LineChart.vue';
|
import PeriodSelector from './PeriodSelector';
|
||||||
import { getYMax } from '@/components/Charts/chartUtils';
|
|
||||||
import { formatXLabels } from '@/utils';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Cashflow',
|
name: 'Cashflow',
|
||||||
|
@ -23,12 +23,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import PageHeader from '@/components/PageHeader';
|
import PageHeader from 'src/components/PageHeader';
|
||||||
import SearchBar from '@/components/SearchBar';
|
import SearchBar from 'src/components/SearchBar';
|
||||||
import Cashflow from './Cashflow';
|
import Cashflow from './Cashflow';
|
||||||
import UnpaidInvoices from './UnpaidInvoices';
|
|
||||||
import ProfitAndLoss from './ProfitAndLoss';
|
|
||||||
import Expenses from './Expenses';
|
import Expenses from './Expenses';
|
||||||
|
import ProfitAndLoss from './ProfitAndLoss';
|
||||||
|
import UnpaidInvoices from './UnpaidInvoices';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Dashboard',
|
name: 'Dashboard',
|
||||||
|
@ -50,8 +50,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import theme from '@/theme';
|
|
||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
|
import theme from 'src/theme';
|
||||||
import DonutChart from '../../components/Charts/DonutChart.vue';
|
import DonutChart from '../../components/Charts/DonutChart.vue';
|
||||||
import { getDatesAndPeriodicity } from './getDatesAndPeriodicity';
|
import { getDatesAndPeriodicity } from './getDatesAndPeriodicity';
|
||||||
import PeriodSelector from './PeriodSelector';
|
import PeriodSelector from './PeriodSelector';
|
||||||
|
@ -36,8 +36,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Dropdown from '@/components/Dropdown';
|
|
||||||
import { t } from 'frappe';
|
import { t } from 'frappe';
|
||||||
|
import Dropdown from 'src/components/Dropdown';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PeriodSelector',
|
name: 'PeriodSelector',
|
||||||
|
@ -29,13 +29,13 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
import PeriodSelector from './PeriodSelector';
|
import BarChart from 'src/components/Charts/BarChart.vue';
|
||||||
import SectionHeader from './SectionHeader';
|
import { getYMax, getYMin } from 'src/components/Charts/chartUtils';
|
||||||
|
import { formatXLabels } from 'src/utils';
|
||||||
import ProfitAndLoss from '../../../reports/ProfitAndLoss/ProfitAndLoss';
|
import ProfitAndLoss from '../../../reports/ProfitAndLoss/ProfitAndLoss';
|
||||||
import { getDatesAndPeriodicity } from './getDatesAndPeriodicity';
|
import { getDatesAndPeriodicity } from './getDatesAndPeriodicity';
|
||||||
import BarChart from '@/components/Charts/BarChart.vue';
|
import PeriodSelector from './PeriodSelector';
|
||||||
import { getYMax, getYMin } from '@/components/Charts/chartUtils';
|
import SectionHeader from './SectionHeader';
|
||||||
import { formatXLabels } from '@/utils';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProfitAndLoss',
|
name: 'ProfitAndLoss',
|
||||||
|
@ -68,9 +68,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import Button from '@/components/Button';
|
|
||||||
import { routeTo } from '@/utils';
|
|
||||||
import frappe, { t } from 'frappe';
|
import frappe, { t } from 'frappe';
|
||||||
|
import Button from 'src/components/Button';
|
||||||
|
import { routeTo } from 'src/utils';
|
||||||
import { getDatesAndPeriodicity } from './getDatesAndPeriodicity';
|
import { getDatesAndPeriodicity } from './getDatesAndPeriodicity';
|
||||||
import PeriodSelector from './PeriodSelector';
|
import PeriodSelector from './PeriodSelector';
|
||||||
import SectionHeader from './SectionHeader';
|
import SectionHeader from './SectionHeader';
|
||||||
|
@ -336,17 +336,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import Button from '@/components/Button.vue';
|
|
||||||
import FormControl from '@/components/Controls/FormControl';
|
|
||||||
import DropdownWithActions from '@/components/DropdownWithActions.vue';
|
|
||||||
import FeatherIcon from '@/components/FeatherIcon.vue';
|
|
||||||
import HowTo from '@/components/HowTo.vue';
|
|
||||||
import PageHeader from '@/components/PageHeader.vue';
|
|
||||||
import { importable, Importer } from '@/dataImport';
|
|
||||||
import { IPC_ACTIONS } from 'utils/messages';
|
|
||||||
import { getSavePath, saveData, showMessageDialog } from '@/utils';
|
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
|
import Button from 'src/components/Button.vue';
|
||||||
|
import FormControl from 'src/components/Controls/FormControl';
|
||||||
|
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
|
||||||
|
import FeatherIcon from 'src/components/FeatherIcon.vue';
|
||||||
|
import HowTo from 'src/components/HowTo.vue';
|
||||||
|
import PageHeader from 'src/components/PageHeader.vue';
|
||||||
|
import { importable, Importer } from 'src/dataImport';
|
||||||
|
import { getSavePath, saveData, showMessageDialog } from 'src/utils';
|
||||||
|
import { IPC_ACTIONS } from 'utils/messages';
|
||||||
import Loading from '../components/Loading.vue';
|
import Loading from '../components/Loading.vue';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -550,7 +550,7 @@ export default {
|
|||||||
if (this.isRequiredUnassigned) {
|
if (this.isRequiredUnassigned) {
|
||||||
showMessageDialog({
|
showMessageDialog({
|
||||||
message: this.t`Required Fields not Assigned`,
|
message: this.t`Required Fields not Assigned`,
|
||||||
description: this
|
detail: this
|
||||||
.t`Please assign the following fields ${this.requiredUnassigned.join(
|
.t`Please assign the following fields ${this.requiredUnassigned.join(
|
||||||
', '
|
', '
|
||||||
)}`,
|
)}`,
|
||||||
@ -561,7 +561,7 @@ export default {
|
|||||||
if (this.importer.assignedMatrix.length === 0) {
|
if (this.importer.assignedMatrix.length === 0) {
|
||||||
showMessageDialog({
|
showMessageDialog({
|
||||||
message: this.t`No Data to Import`,
|
message: this.t`No Data to Import`,
|
||||||
description: this.t`Please select a file with data to import.`,
|
detail: this.t`Please select a file with data to import.`,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -572,7 +572,7 @@ export default {
|
|||||||
if (!success) {
|
if (!success) {
|
||||||
showMessageDialog({
|
showMessageDialog({
|
||||||
message: this.t`Import Failed`,
|
message: this.t`Import Failed`,
|
||||||
description: message,
|
detail: message,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -617,7 +617,7 @@ export default {
|
|||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
showMessageDialog({
|
showMessageDialog({
|
||||||
message: this.t`Bad import data.`,
|
message: this.t`Bad import data.`,
|
||||||
description: this.t`Could not select file.`,
|
detail: this.t`Could not select file.`,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -158,14 +158,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import LanguageSelector from '@/components/Controls/LanguageSelector.vue';
|
|
||||||
import config from '@/config';
|
|
||||||
import { connectToLocalDatabase, createNewDatabase } from '@/initialization';
|
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { showErrorDialog } from '../errorHandling';
|
import LanguageSelector from 'src/components/Controls/LanguageSelector.vue';
|
||||||
|
import config from 'src/config';
|
||||||
|
import { connectToLocalDatabase, createNewDatabase } from 'src/initialization';
|
||||||
import { DB_CONN_FAILURE, IPC_ACTIONS } from '../../utils/messages';
|
import { DB_CONN_FAILURE, IPC_ACTIONS } from '../../utils/messages';
|
||||||
|
import { showErrorDialog } from '../errorHandling';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DatabaseSelector',
|
name: 'DatabaseSelector',
|
||||||
|
@ -84,12 +84,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Button from '@/components/Button';
|
|
||||||
import Icon from '@/components/Icon';
|
|
||||||
import PageHeader from '@/components/PageHeader';
|
|
||||||
import { openSettings, routeTo } from '@/utils';
|
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import frappe, { t } from 'frappe';
|
import frappe, { t } from 'frappe';
|
||||||
|
import Button from 'src/components/Button';
|
||||||
|
import Icon from 'src/components/Icon';
|
||||||
|
import PageHeader from 'src/components/PageHeader';
|
||||||
|
import { openSettings, routeTo } from 'src/utils';
|
||||||
import { IPC_MESSAGES } from 'utils/messages';
|
import { IPC_MESSAGES } from 'utils/messages';
|
||||||
import { h } from 'vue';
|
import { h } from 'vue';
|
||||||
|
|
||||||
|
@ -198,20 +198,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import BackLink from '@/components/BackLink';
|
import frappe from 'frappe';
|
||||||
import Button from '@/components/Button';
|
import { getInvoiceStatus } from 'models/helpers';
|
||||||
import FormControl from '@/components/Controls/FormControl';
|
import BackLink from 'src/components/BackLink';
|
||||||
import DropdownWithActions from '@/components/DropdownWithActions';
|
import Button from 'src/components/Button';
|
||||||
import PageHeader from '@/components/PageHeader';
|
import FormControl from 'src/components/Controls/FormControl';
|
||||||
import StatusBadge from '@/components/StatusBadge';
|
import DropdownWithActions from 'src/components/DropdownWithActions';
|
||||||
|
import PageHeader from 'src/components/PageHeader';
|
||||||
|
import StatusBadge from 'src/components/StatusBadge';
|
||||||
import {
|
import {
|
||||||
getActionsForDocument,
|
getActionsForDocument,
|
||||||
getInvoiceStatus,
|
|
||||||
openSettings,
|
openSettings,
|
||||||
routeTo,
|
routeTo,
|
||||||
showMessageDialog
|
showMessageDialog
|
||||||
} from '@/utils';
|
} from 'src/utils';
|
||||||
import frappe from 'frappe';
|
|
||||||
import { handleErrorWithDialog } from '../errorHandling';
|
import { handleErrorWithDialog } from '../errorHandling';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -128,14 +128,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import BackLink from '@/components/BackLink';
|
|
||||||
import Button from '@/components/Button';
|
|
||||||
import FormControl from '@/components/Controls/FormControl';
|
|
||||||
import DropdownWithActions from '@/components/DropdownWithActions';
|
|
||||||
import PageHeader from '@/components/PageHeader';
|
|
||||||
import StatusBadge from '@/components/StatusBadge';
|
|
||||||
import { getActionsForDocument, routeTo, showMessageDialog } from '@/utils';
|
|
||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
|
import BackLink from 'src/components/BackLink';
|
||||||
|
import Button from 'src/components/Button';
|
||||||
|
import FormControl from 'src/components/Controls/FormControl';
|
||||||
|
import DropdownWithActions from 'src/components/DropdownWithActions';
|
||||||
|
import PageHeader from 'src/components/PageHeader';
|
||||||
|
import StatusBadge from 'src/components/StatusBadge';
|
||||||
|
import { getActionsForDocument, routeTo, showMessageDialog } from 'src/utils';
|
||||||
import { handleErrorWithDialog } from '../errorHandling';
|
import { handleErrorWithDialog } from '../errorHandling';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -56,11 +56,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import Avatar from '@/components/Avatar';
|
|
||||||
import Button from '@/components/Button';
|
|
||||||
import Row from '@/components/Row';
|
|
||||||
import { openQuickEdit, routeTo } from '@/utils';
|
|
||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
|
import Avatar from 'src/components/Avatar';
|
||||||
|
import Button from 'src/components/Button';
|
||||||
|
import Row from 'src/components/Row';
|
||||||
|
import { openQuickEdit, routeTo } from 'src/utils';
|
||||||
import ListCell from './ListCell';
|
import ListCell from './ListCell';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -30,12 +30,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import Button from '@/components/Button';
|
|
||||||
import FilterDropdown from '@/components/FilterDropdown';
|
|
||||||
import PageHeader from '@/components/PageHeader';
|
|
||||||
import SearchBar from '@/components/SearchBar';
|
|
||||||
import { routeTo } from '@/utils';
|
|
||||||
import frappe from 'fyo';
|
import frappe from 'fyo';
|
||||||
|
import Button from 'src/components/Button';
|
||||||
|
import FilterDropdown from 'src/components/FilterDropdown';
|
||||||
|
import PageHeader from 'src/components/PageHeader';
|
||||||
|
import SearchBar from 'src/components/SearchBar';
|
||||||
|
import { routeTo } from 'src/utils';
|
||||||
import List from './List';
|
import List from './List';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -51,16 +51,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import BackLink from '@/components/BackLink';
|
|
||||||
import Button from '@/components/Button';
|
|
||||||
import PageHeader from '@/components/PageHeader';
|
|
||||||
import SearchBar from '@/components/SearchBar';
|
|
||||||
import TwoColumnForm from '@/components/TwoColumnForm';
|
|
||||||
import telemetry from '@/telemetry/telemetry';
|
|
||||||
import { Verb } from '@/telemetry/types';
|
|
||||||
import { makePDF } from '@/utils';
|
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import frappe from 'fyo';
|
import frappe from 'fyo';
|
||||||
|
import BackLink from 'src/components/BackLink';
|
||||||
|
import Button from 'src/components/Button';
|
||||||
|
import PageHeader from 'src/components/PageHeader';
|
||||||
|
import SearchBar from 'src/components/SearchBar';
|
||||||
|
import TwoColumnForm from 'src/components/TwoColumnForm';
|
||||||
|
import telemetry from 'src/telemetry/telemetry';
|
||||||
|
import { Verb } from 'src/telemetry/types';
|
||||||
|
import { makePDF } from 'src/utils';
|
||||||
import { IPC_ACTIONS } from 'utils/messages';
|
import { IPC_ACTIONS } from 'utils/messages';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -77,13 +77,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Button from '@/components/Button';
|
|
||||||
import FormControl from '@/components/Controls/FormControl';
|
|
||||||
import DropdownWithActions from '@/components/DropdownWithActions';
|
|
||||||
import StatusBadge from '@/components/StatusBadge';
|
|
||||||
import TwoColumnForm from '@/components/TwoColumnForm';
|
|
||||||
import { getActionsForDocument, openQuickEdit } from '@/utils';
|
|
||||||
import frappe, { t } from 'frappe';
|
import frappe, { t } from 'frappe';
|
||||||
|
import Button from 'src/components/Button';
|
||||||
|
import FormControl from 'src/components/Controls/FormControl';
|
||||||
|
import DropdownWithActions from 'src/components/DropdownWithActions';
|
||||||
|
import StatusBadge from 'src/components/StatusBadge';
|
||||||
|
import TwoColumnForm from 'src/components/TwoColumnForm';
|
||||||
|
import { getActionsForDocument, openQuickEdit } from 'src/utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'QuickEditForm',
|
name: 'QuickEditForm',
|
||||||
|
@ -136,15 +136,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import reportViewConfig from '@/../reports/view';
|
|
||||||
import Button from '@/components/Button';
|
|
||||||
import FormControl from '@/components/Controls/FormControl';
|
|
||||||
import FeatherIcon from '@/components/FeatherIcon.vue';
|
|
||||||
import PageHeader from '@/components/PageHeader';
|
|
||||||
import Row from '@/components/Row';
|
|
||||||
import SearchBar from '@/components/SearchBar';
|
|
||||||
import WithScroll from '@/components/WithScroll';
|
|
||||||
import frappe from 'frappe';
|
import frappe from 'frappe';
|
||||||
|
import reportViewConfig from 'src/../reports/view';
|
||||||
|
import Button from 'src/components/Button';
|
||||||
|
import FormControl from 'src/components/Controls/FormControl';
|
||||||
|
import FeatherIcon from 'src/components/FeatherIcon.vue';
|
||||||
|
import PageHeader from 'src/components/PageHeader';
|
||||||
|
import Row from 'src/components/Row';
|
||||||
|
import SearchBar from 'src/components/SearchBar';
|
||||||
|
import WithScroll from 'src/components/WithScroll';
|
||||||
import { h, markRaw } from 'vue';
|
import { h, markRaw } from 'vue';
|
||||||
import { getReportData } from '../../reports/index';
|
import { getReportData } from '../../reports/index';
|
||||||
import DropdownWithActions from '../components/DropdownWithActions.vue';
|
import DropdownWithActions from '../components/DropdownWithActions.vue';
|
||||||
|
@ -51,14 +51,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import Button from '@/components/Button';
|
|
||||||
import Icon from '@/components/Icon';
|
|
||||||
import PageHeader from '@/components/PageHeader';
|
|
||||||
import Row from '@/components/Row';
|
|
||||||
import StatusBadge from '@/components/StatusBadge';
|
|
||||||
import WindowControls from '@/components/WindowControls';
|
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import frappe, { t } from 'fyo';
|
import frappe, { t } from 'fyo';
|
||||||
|
import Button from 'src/components/Button';
|
||||||
|
import Icon from 'src/components/Icon';
|
||||||
|
import PageHeader from 'src/components/PageHeader';
|
||||||
|
import Row from 'src/components/Row';
|
||||||
|
import StatusBadge from 'src/components/StatusBadge';
|
||||||
|
import WindowControls from 'src/components/WindowControls';
|
||||||
import { IPC_MESSAGES } from 'utils/messages';
|
import { IPC_MESSAGES } from 'utils/messages';
|
||||||
import { h, markRaw } from 'vue';
|
import { h, markRaw } from 'vue';
|
||||||
import { callInitializeMoneyMaker, showToast } from '../../utils';
|
import { callInitializeMoneyMaker, showToast } from '../../utils';
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TwoColumnForm from '@/components/TwoColumnForm';
|
|
||||||
import frappe from 'fyo';
|
import frappe from 'fyo';
|
||||||
|
import TwoColumnForm from 'src/components/TwoColumnForm';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TabGeneral',
|
name: 'TabGeneral',
|
||||||
|
@ -46,10 +46,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import FormControl from '@/components/Controls/FormControl';
|
|
||||||
import TwoColumnForm from '@/components/TwoColumnForm';
|
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import frappe from 'fyo';
|
import frappe from 'fyo';
|
||||||
|
import FormControl from 'src/components/Controls/FormControl';
|
||||||
|
import TwoColumnForm from 'src/components/TwoColumnForm';
|
||||||
import { IPC_ACTIONS } from 'utils/messages';
|
import { IPC_ACTIONS } from 'utils/messages';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -38,17 +38,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import FormControl from '@/components/Controls/FormControl';
|
import frappe from 'fyo';
|
||||||
import LanguageSelector from '@/components/Controls/LanguageSelector.vue';
|
import FormControl from 'src/components/Controls/FormControl';
|
||||||
import TwoColumnForm from '@/components/TwoColumnForm';
|
import LanguageSelector from 'src/components/Controls/LanguageSelector.vue';
|
||||||
|
import TwoColumnForm from 'src/components/TwoColumnForm';
|
||||||
import config, {
|
import config, {
|
||||||
ConfigKeys,
|
ConfigKeys,
|
||||||
TelemetrySetting
|
TelemetrySetting
|
||||||
} from '@/config';
|
} from 'src/config';
|
||||||
import { getTelemetryOptions } from '@/telemetry/helpers';
|
import { getTelemetryOptions } from 'src/telemetry/helpers';
|
||||||
import telemetry from '@/telemetry/telemetry';
|
import telemetry from 'src/telemetry/telemetry';
|
||||||
import { checkForUpdates } from '@/utils';
|
import { checkForUpdates } from 'src/utils';
|
||||||
import frappe from 'fyo';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TabSystem',
|
name: 'TabSystem',
|
||||||
|
@ -89,17 +89,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import FormControl from '@/components/Controls/FormControl';
|
|
||||||
import LanguageSelector from '@/components/Controls/LanguageSelector.vue';
|
|
||||||
import Popover from '@/components/Popover';
|
|
||||||
import TwoColumnForm from '@/components/TwoColumnForm';
|
|
||||||
import config from '@/config';
|
|
||||||
import { connectToLocalDatabase, purgeCache } from '@/initialization';
|
|
||||||
import { setLanguageMap, showMessageDialog } from '@/utils';
|
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import frappe from 'fyo';
|
import frappe from 'fyo';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import FormControl from 'src/components/Controls/FormControl';
|
||||||
|
import LanguageSelector from 'src/components/Controls/LanguageSelector.vue';
|
||||||
|
import Popover from 'src/components/Popover';
|
||||||
|
import TwoColumnForm from 'src/components/TwoColumnForm';
|
||||||
|
import config from 'src/config';
|
||||||
|
import { connectToLocalDatabase, purgeCache } from 'src/initialization';
|
||||||
|
import { setLanguageMap, showMessageDialog } from 'src/utils';
|
||||||
import { IPC_MESSAGES } from 'utils/messages';
|
import { IPC_MESSAGES } from 'utils/messages';
|
||||||
import {
|
import {
|
||||||
getErrorMessage,
|
getErrorMessage,
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Button from '@/components/Button.vue';
|
import Button from 'src/components/Button.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
emits: ['primary-clicked', 'secondary-clicked'],
|
emits: ['primary-clicked', 'secondary-clicked'],
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import config from '@/config';
|
import countryList from 'fixtures/countryInfo.json';
|
||||||
import frappe from 'fyo';
|
import frappe from 'fyo';
|
||||||
import { DEFAULT_LOCALE } from 'fyo/utils/consts';
|
import { DEFAULT_LOCALE } from 'fyo/utils/consts';
|
||||||
import countryList from '~/fixtures/countryInfo.json';
|
import config from 'src/config';
|
||||||
import importCharts from '../../../accounting/importCOA';
|
import importCharts from '../../../accounting/importCOA';
|
||||||
import generateTaxes from '../../../models/doctype/Tax/RegionalEntries';
|
import generateTaxes from '../../../models/doctype/Tax/RegionalEntries';
|
||||||
import regionalModelUpdates from '../../../models/regionalModelUpdates';
|
import regionalModelUpdates from '../../../models/regionalModelUpdates';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createNumberSeries } from 'frappe/model/naming';
|
import { createNumberSeries } from 'fyo/model/naming';
|
||||||
import { DEFAULT_SERIES_START } from 'frappe/utils/consts';
|
import { DEFAULT_SERIES_START } from 'fyo/utils/consts';
|
||||||
import { getValueMapFromList } from 'utils';
|
import { getValueMapFromList } from 'utils';
|
||||||
import { fyo } from './initFyo';
|
import { fyo } from './initFyo';
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { fyo } from '@/initFyo';
|
import { fyo } from 'src/initFyo';
|
||||||
|
|
||||||
export type TaxType = 'GST' | 'IGST' | 'Exempt-GST' | 'Exempt-IGST';
|
export type TaxType = 'GST' | 'IGST' | 'Exempt-GST' | 'Exempt-IGST';
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { fyo } from '@/initFyo';
|
|
||||||
import { ConfigKeys } from 'fyo/core/types';
|
import { ConfigKeys } from 'fyo/core/types';
|
||||||
|
import { fyo } from 'src/initFyo';
|
||||||
|
|
||||||
export function incrementOpenCount() {
|
export function incrementOpenCount() {
|
||||||
let openCount = fyo.config.get(ConfigKeys.OpenCount);
|
let openCount = fyo.config.get(ConfigKeys.OpenCount);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { handleError } from '@/errorHandling';
|
|
||||||
import { fyo } from '@/initFyo';
|
|
||||||
import { showToast } from '@/utils';
|
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
|
import { handleError } from 'src/errorHandling';
|
||||||
|
import { fyo } from 'src/initFyo';
|
||||||
|
import { showToast } from 'src/utils/ui';
|
||||||
import { IPC_CHANNELS, IPC_MESSAGES } from 'utils/messages';
|
import { IPC_CHANNELS, IPC_MESSAGES } from 'utils/messages';
|
||||||
|
|
||||||
export default function registerIpcRendererListeners() {
|
export default function registerIpcRendererListeners() {
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import ChartOfAccounts from '@/pages/ChartOfAccounts.vue';
|
import { NounEnum, Verb } from 'fyo/telemetry/types';
|
||||||
import Dashboard from '@/pages/Dashboard/Dashboard.vue';
|
import ChartOfAccounts from 'src/pages/ChartOfAccounts.vue';
|
||||||
import DataImport from '@/pages/DataImport.vue';
|
import Dashboard from 'src/pages/Dashboard/Dashboard.vue';
|
||||||
import GetStarted from '@/pages/GetStarted.vue';
|
import DataImport from 'src/pages/DataImport.vue';
|
||||||
import InvoiceForm from '@/pages/InvoiceForm.vue';
|
import GetStarted from 'src/pages/GetStarted.vue';
|
||||||
import JournalEntryForm from '@/pages/JournalEntryForm.vue';
|
import InvoiceForm from 'src/pages/InvoiceForm.vue';
|
||||||
import ListView from '@/pages/ListView/ListView.vue';
|
import JournalEntryForm from 'src/pages/JournalEntryForm.vue';
|
||||||
import PrintView from '@/pages/PrintView/PrintView.vue';
|
import ListView from 'src/pages/ListView/ListView.vue';
|
||||||
import QuickEditForm from '@/pages/QuickEditForm.vue';
|
import PrintView from 'src/pages/PrintView/PrintView.vue';
|
||||||
import Report from '@/pages/Report.vue';
|
import QuickEditForm from 'src/pages/QuickEditForm.vue';
|
||||||
import Settings from '@/pages/Settings/Settings.vue';
|
import Report from 'src/pages/Report.vue';
|
||||||
|
import Settings from 'src/pages/Settings/Settings.vue';
|
||||||
import { createRouter, createWebHistory } from 'vue-router';
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
import telemetry from '../frappe/telemetry/telemetry';
|
import { fyo } from './initFyo';
|
||||||
import { NounEnum, Verb } from '../frappe/telemetry/types';
|
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
@ -112,7 +112,7 @@ const routes = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let router = createRouter({ routes, history: createWebHistory() });
|
const router = createRouter({ routes, history: createWebHistory() });
|
||||||
|
|
||||||
function removeDetails(path) {
|
function removeDetails(path) {
|
||||||
if (!path) {
|
if (!path) {
|
||||||
@ -133,7 +133,7 @@ router.afterEach((to, from) => {
|
|||||||
to: removeDetails(to.fullPath),
|
to: removeDetails(to.fullPath),
|
||||||
};
|
};
|
||||||
|
|
||||||
telemetry.log(Verb.Navigated, NounEnum.Route, more);
|
fyo.telemetry.log(Verb.Navigated, NounEnum.Route, more);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
603
src/utils.js
603
src/utils.js
@ -1,603 +0,0 @@
|
|||||||
import Avatar from '@/components/Avatar.vue';
|
|
||||||
import Toast from '@/components/Toast.vue';
|
|
||||||
import router from '@/router';
|
|
||||||
import { ipcRenderer } from 'electron';
|
|
||||||
import frappe, { t } from 'frappe';
|
|
||||||
import { isPesa } from 'frappe/utils';
|
|
||||||
import { DEFAULT_LANGUAGE } from 'frappe/utils/consts';
|
|
||||||
import { setLanguageMapOnTranslationString } from 'frappe/utils/translation';
|
|
||||||
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
|
|
||||||
import { createApp, h } from 'vue';
|
|
||||||
import config from './config';
|
|
||||||
import { handleErrorWithDialog } from './errorHandling';
|
|
||||||
import { languageCodeMap } from './languageCodeMap';
|
|
||||||
|
|
||||||
export async function showMessageDialog({
|
|
||||||
message,
|
|
||||||
description,
|
|
||||||
buttons = [],
|
|
||||||
}) {
|
|
||||||
const options = {
|
|
||||||
message,
|
|
||||||
detail: description,
|
|
||||||
buttons: buttons.map((a) => a.label),
|
|
||||||
};
|
|
||||||
|
|
||||||
const { response } = await ipcRenderer.invoke(
|
|
||||||
IPC_ACTIONS.GET_DIALOG_RESPONSE,
|
|
||||||
options
|
|
||||||
);
|
|
||||||
|
|
||||||
let button = buttons[response];
|
|
||||||
if (button && button.action) {
|
|
||||||
button.action();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteDocWithPrompt(doc) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
showMessageDialog({
|
|
||||||
message: t`Are you sure you want to delete ${doc.doctype} ${doc.name}?`,
|
|
||||||
description: t`This action is permanent`,
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
label: t`Delete`,
|
|
||||||
action: () => {
|
|
||||||
doc
|
|
||||||
.delete()
|
|
||||||
.then(() => resolve(true))
|
|
||||||
.catch((e) => {
|
|
||||||
handleErrorWithDialog(e, doc);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Cancel`,
|
|
||||||
action() {
|
|
||||||
resolve(false);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function cancelDocWithPrompt(doc) {
|
|
||||||
let description = t`This action is permanent`;
|
|
||||||
if (['SalesInvoice', 'PurchaseInvoice'].includes(doc.doctype)) {
|
|
||||||
const payments = (
|
|
||||||
await frappe.db.getAll('Payment', {
|
|
||||||
fields: ['name'],
|
|
||||||
filters: { cancelled: false },
|
|
||||||
})
|
|
||||||
).map(({ name }) => name);
|
|
||||||
|
|
||||||
const query = (
|
|
||||||
await frappe.db.getAll('PaymentFor', {
|
|
||||||
fields: ['parent'],
|
|
||||||
filters: {
|
|
||||||
referenceName: doc.name,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
).filter(({ parent }) => payments.includes(parent));
|
|
||||||
|
|
||||||
const paymentList = [...new Set(query.map(({ parent }) => parent))];
|
|
||||||
|
|
||||||
if (paymentList.length === 1) {
|
|
||||||
description = t`This action is permanent and will cancel the following payment: ${paymentList[0]}`;
|
|
||||||
} else if (paymentList.length > 1) {
|
|
||||||
description = t`This action is permanent and will cancel the following payments: ${paymentList.join(
|
|
||||||
', '
|
|
||||||
)}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
showMessageDialog({
|
|
||||||
message: t`Are you sure you want to cancel ${doc.doctype} ${doc.name}?`,
|
|
||||||
description,
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
label: t`Yes`,
|
|
||||||
async action() {
|
|
||||||
const entryDoc = await frappe.doc.getDoc(doc.doctype, doc.name);
|
|
||||||
entryDoc.cancelled = 1;
|
|
||||||
await entryDoc.update();
|
|
||||||
entryDoc
|
|
||||||
.revert()
|
|
||||||
.then(() => resolve(true))
|
|
||||||
.catch((e) => {
|
|
||||||
handleErrorWithDialog(e, doc);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`No`,
|
|
||||||
action() {
|
|
||||||
resolve(false);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function partyWithAvatar(party) {
|
|
||||||
return {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
imageURL: null,
|
|
||||||
label: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
Avatar,
|
|
||||||
},
|
|
||||||
async mounted() {
|
|
||||||
const p = await frappe.db.get('Party', party);
|
|
||||||
this.imageURL = p.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>
|
|
||||||
`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getShowFields(doctype) {
|
|
||||||
if (doctype === 'Party') {
|
|
||||||
return ['customer'];
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function openQuickEdit({
|
|
||||||
doctype,
|
|
||||||
name,
|
|
||||||
hideFields,
|
|
||||||
showFields,
|
|
||||||
defaults = {},
|
|
||||||
}) {
|
|
||||||
let currentRoute = router.currentRoute.value;
|
|
||||||
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';
|
|
||||||
}
|
|
||||||
if (query.name === name) return;
|
|
||||||
|
|
||||||
if (defaults?.for?.[0] === 'not in') {
|
|
||||||
const purpose = defaults.for?.[1]?.[0];
|
|
||||||
defaults = Object.assign({
|
|
||||||
for:
|
|
||||||
purpose === 'sales'
|
|
||||||
? 'purchases'
|
|
||||||
: purpose === 'purchases'
|
|
||||||
? 'sales'
|
|
||||||
: 'both',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defaults?.for?.[0] === 'not in' && defaults?.for?.[1] === 'sales') {
|
|
||||||
defaults = Object.assign({ for: 'purchases' });
|
|
||||||
}
|
|
||||||
|
|
||||||
router[method]({
|
|
||||||
query: {
|
|
||||||
edit: 1,
|
|
||||||
doctype,
|
|
||||||
name,
|
|
||||||
showFields: showFields ?? getShowFields(doctype),
|
|
||||||
hideFields,
|
|
||||||
valueJSON: stringifyCircular(defaults),
|
|
||||||
lastRoute: currentRoute,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function makePDF(html, savePath) {
|
|
||||||
await ipcRenderer.invoke(IPC_ACTIONS.SAVE_HTML_AS_PDF, html, savePath);
|
|
||||||
showExportInFolder(frappe.t`Save as PDF Successful`, savePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function showExportInFolder(message, filePath) {
|
|
||||||
showToast({
|
|
||||||
message,
|
|
||||||
actionText: frappe.t`Open Folder`,
|
|
||||||
type: 'success',
|
|
||||||
action: async () => {
|
|
||||||
await showItemInFolder(filePath);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function saveData(data, savePath) {
|
|
||||||
await ipcRenderer.invoke(IPC_ACTIONS.SAVE_DATA, data, savePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function showItemInFolder(filePath) {
|
|
||||||
await ipcRenderer.send(IPC_MESSAGES.SHOW_ITEM_IN_FOLDER, filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getActionsForDocument(doc) {
|
|
||||||
if (!doc) return [];
|
|
||||||
|
|
||||||
let deleteAction = {
|
|
||||||
component: {
|
|
||||||
template: '<span class="text-red-700">{{ t`Delete` }}</span>',
|
|
||||||
},
|
|
||||||
condition: (doc) =>
|
|
||||||
!doc.isNew() && !doc.submitted && !doc.meta.isSingle && !doc.cancelled,
|
|
||||||
action: () =>
|
|
||||||
deleteDocWithPrompt(doc).then((res) => {
|
|
||||||
if (res) {
|
|
||||||
routeTo(`/list/${doc.doctype}`);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
let cancelAction = {
|
|
||||||
component: {
|
|
||||||
template: '<span class="text-red-700">{{ t`Cancel` }}</span>',
|
|
||||||
},
|
|
||||||
condition: (doc) => doc.submitted && !doc.cancelled,
|
|
||||||
action: () => {
|
|
||||||
cancelDocWithPrompt(doc).then((res) => {
|
|
||||||
if (res) {
|
|
||||||
router.push(`/list/${doc.doctype}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const isSubmittable = !!doc.meta.isSubmittable;
|
|
||||||
const duplicateAction = {
|
|
||||||
label: frappe.t`Duplicate`,
|
|
||||||
condition: (doc) =>
|
|
||||||
((isSubmittable && doc && doc.submitted) || !isSubmittable) &&
|
|
||||||
!doc._notInserted &&
|
|
||||||
!(doc.cancelled || false),
|
|
||||||
action: () => {
|
|
||||||
showMessageDialog({
|
|
||||||
message: t`Duplicate ${doc.doctype} ${doc.name}?`,
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
label: t`Yes`,
|
|
||||||
async action() {
|
|
||||||
doc.duplicate();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`No`,
|
|
||||||
action() {
|
|
||||||
resolve(false);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let actions = [
|
|
||||||
...(doc.meta.actions || []),
|
|
||||||
duplicateAction,
|
|
||||||
deleteAction,
|
|
||||||
cancelAction,
|
|
||||||
]
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function runWindowAction(name) {
|
|
||||||
switch (name) {
|
|
||||||
case 'close':
|
|
||||||
ipcRenderer.send(IPC_MESSAGES.CLOSE_CURRENT_WINDOW);
|
|
||||||
break;
|
|
||||||
case 'minimize':
|
|
||||||
ipcRenderer.send(IPC_MESSAGES.MINIMIZE_CURRENT_WINDOW);
|
|
||||||
break;
|
|
||||||
case 'maximize':
|
|
||||||
const maximizing = await ipcRenderer.invoke(
|
|
||||||
IPC_ACTIONS.TOGGLE_MAXIMIZE_CURRENT_WINDOW
|
|
||||||
);
|
|
||||||
name = maximizing ? name : 'unmaximize';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getInvoiceStatus(doc) {
|
|
||||||
let status = `Unpaid`;
|
|
||||||
if (!doc.submitted) {
|
|
||||||
status = 'Draft';
|
|
||||||
}
|
|
||||||
if (doc.submitted === 1 && doc.outstandingAmount.isZero()) {
|
|
||||||
status = 'Paid';
|
|
||||||
}
|
|
||||||
if (doc.cancelled === 1) {
|
|
||||||
status = 'Cancelled';
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function routeTo(route) {
|
|
||||||
let routeOptions = route;
|
|
||||||
if (
|
|
||||||
typeof route === 'string' &&
|
|
||||||
route === router.currentRoute.value.fullPath
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof route === 'string') {
|
|
||||||
routeOptions = { path: route };
|
|
||||||
}
|
|
||||||
|
|
||||||
router.push(routeOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fuzzyMatch(keyword, candidate) {
|
|
||||||
const keywordLetters = [...keyword];
|
|
||||||
const candidateLetters = [...candidate];
|
|
||||||
|
|
||||||
let keywordLetter = keywordLetters.shift();
|
|
||||||
let candidateLetter = candidateLetters.shift();
|
|
||||||
|
|
||||||
let isMatch = true;
|
|
||||||
let distance = 0;
|
|
||||||
|
|
||||||
while (keywordLetter && candidateLetter) {
|
|
||||||
if (keywordLetter.toLowerCase() === candidateLetter.toLowerCase()) {
|
|
||||||
keywordLetter = keywordLetters.shift();
|
|
||||||
} else {
|
|
||||||
distance += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
candidateLetter = candidateLetters.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keywordLetter !== undefined) {
|
|
||||||
distance = -1;
|
|
||||||
isMatch = false;
|
|
||||||
} else {
|
|
||||||
distance += candidateLetters.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { isMatch, distance };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function openSettings(tab) {
|
|
||||||
routeTo({ path: '/settings', query: { tab } });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getSavePath(name, extention) {
|
|
||||||
let { canceled, filePath } = await ipcRenderer.invoke(
|
|
||||||
IPC_ACTIONS.GET_SAVE_FILEPATH,
|
|
||||||
{
|
|
||||||
title: t`Select Folder`,
|
|
||||||
defaultPath: `${name}.${extention}`,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (filePath && !filePath.endsWith(extention)) {
|
|
||||||
filePath = filePath + extention;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { canceled, filePath };
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceAndAppendMount(app, replaceId) {
|
|
||||||
const fragment = document.createDocumentFragment();
|
|
||||||
const target = document.getElementById(replaceId);
|
|
||||||
if (target === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parent = target.parentElement;
|
|
||||||
const clone = target.cloneNode();
|
|
||||||
|
|
||||||
app.mount(fragment);
|
|
||||||
target.replaceWith(fragment);
|
|
||||||
parent.append(clone);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function showToast(props) {
|
|
||||||
const toast = createApp({
|
|
||||||
render() {
|
|
||||||
return h(Toast, { ...props });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
replaceAndAppendMount(toast, 'toast-target');
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getIsSetupComplete() {
|
|
||||||
try {
|
|
||||||
const { setupComplete } = await frappe.getSingle('AccountingSettings');
|
|
||||||
return !!setupComplete;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getCurrency() {
|
|
||||||
let currency = frappe?.AccountingSettings?.currency ?? undefined;
|
|
||||||
|
|
||||||
if (!currency) {
|
|
||||||
try {
|
|
||||||
currency = (
|
|
||||||
await frappe.db.getSingleValues({
|
|
||||||
fieldname: 'currency',
|
|
||||||
parent: 'AccountingSettings',
|
|
||||||
})
|
|
||||||
)[0].value;
|
|
||||||
} catch (err) {
|
|
||||||
currency = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return currency;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function callInitializeMoneyMaker(currency, force = false) {
|
|
||||||
currency ??= await getCurrency();
|
|
||||||
if (!force && !currency && frappe.pesa) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!force && currency && frappe.pesa().options.currency === currency) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await frappe.initializeMoneyMaker(currency);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function convertPesaValuesToFloat(obj) {
|
|
||||||
Object.keys(obj).forEach((key) => {
|
|
||||||
if (!isPesa(obj[key])) return;
|
|
||||||
|
|
||||||
obj[key] = obj[key].float;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatXLabels(label) {
|
|
||||||
// Format: Mmm YYYY -> Mm YY
|
|
||||||
let [month, year] = label.split(' ');
|
|
||||||
year = year.slice(2);
|
|
||||||
|
|
||||||
return `${month} ${year}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function stringifyCircular(
|
|
||||||
obj,
|
|
||||||
ignoreCircular = false,
|
|
||||||
convertDocument = false
|
|
||||||
) {
|
|
||||||
const cacheKey = [];
|
|
||||||
const cacheValue = [];
|
|
||||||
|
|
||||||
return JSON.stringify(obj, (key, value) => {
|
|
||||||
if (typeof value !== 'object' || value === null) {
|
|
||||||
cacheKey.push(key);
|
|
||||||
cacheValue.push(value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cacheValue.includes(value)) {
|
|
||||||
const circularKey = cacheKey[cacheValue.indexOf(value)] || '{self}';
|
|
||||||
return ignoreCircular ? undefined : `[Circular:${circularKey}]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheKey.push(key);
|
|
||||||
cacheValue.push(value);
|
|
||||||
|
|
||||||
if (convertDocument && value instanceof frappe.Document) {
|
|
||||||
return value.getValidDict();
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function checkForUpdates(force = false) {
|
|
||||||
ipcRenderer.invoke(IPC_ACTIONS.CHECK_FOR_UPDATES, force);
|
|
||||||
await setLanguageMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchAndSetLanguageMap(code) {
|
|
||||||
const { success, message, languageMap } = await ipcRenderer.invoke(
|
|
||||||
IPC_ACTIONS.GET_LANGUAGE_MAP,
|
|
||||||
code
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
showToast({ type: 'error', message });
|
|
||||||
} else {
|
|
||||||
setLanguageMapOnTranslationString(languageMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function setLanguageMap(initLanguage, dontReload = false) {
|
|
||||||
const oldLanguage = config.get('language');
|
|
||||||
initLanguage ??= oldLanguage;
|
|
||||||
const [code, language, usingDefault] = getLanguageCode(
|
|
||||||
initLanguage,
|
|
||||||
oldLanguage
|
|
||||||
);
|
|
||||||
|
|
||||||
let success = true;
|
|
||||||
if (code === 'en') {
|
|
||||||
setLanguageMapOnTranslationString(undefined);
|
|
||||||
} else {
|
|
||||||
success = await fetchAndSetLanguageMap(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (success && !usingDefault) {
|
|
||||||
config.set('language', language);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dontReload && success && initLanguage !== oldLanguage) {
|
|
||||||
await ipcRenderer.send(IPC_MESSAGES.RELOAD_MAIN_WINDOW);
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLanguageCode(initLanguage, oldLanguage) {
|
|
||||||
let language = initLanguage ?? oldLanguage;
|
|
||||||
let usingDefault = false;
|
|
||||||
|
|
||||||
if (!language) {
|
|
||||||
language = DEFAULT_LANGUAGE;
|
|
||||||
usingDefault = true;
|
|
||||||
}
|
|
||||||
const code = languageCodeMap[language] ?? 'en';
|
|
||||||
return [code, language, usingDefault];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCOAList() {
|
|
||||||
if (!frappe.temp.coaList) {
|
|
||||||
const coaList = [
|
|
||||||
{ name: t`Standard Chart of Accounts`, countryCode: '' },
|
|
||||||
|
|
||||||
{ countryCode: 'ae', name: 'U.A.E - Chart of Accounts' },
|
|
||||||
{
|
|
||||||
countryCode: 'ca',
|
|
||||||
name: 'Canada - Plan comptable pour les provinces francophones',
|
|
||||||
},
|
|
||||||
{ countryCode: 'gt', name: 'Guatemala - Cuentas' },
|
|
||||||
{ countryCode: 'hu', name: 'Hungary - Chart of Accounts' },
|
|
||||||
{ countryCode: 'id', name: 'Indonesia - Chart of Accounts' },
|
|
||||||
{ countryCode: 'in', name: 'India - Chart of Accounts' },
|
|
||||||
{ countryCode: 'mx', name: 'Mexico - Plan de Cuentas' },
|
|
||||||
{ countryCode: 'ni', name: 'Nicaragua - Catalogo de Cuentas' },
|
|
||||||
{ countryCode: 'nl', name: 'Netherlands - Grootboekschema' },
|
|
||||||
{ countryCode: 'sg', name: 'Singapore - Chart of Accounts' },
|
|
||||||
];
|
|
||||||
frappe.temp.coaList = coaList;
|
|
||||||
}
|
|
||||||
return frappe.temp.coaList;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function invertMap(map) {
|
|
||||||
const keys = Object.keys(map);
|
|
||||||
const inverted = {};
|
|
||||||
for (const key of keys) {
|
|
||||||
const val = map[key];
|
|
||||||
inverted[val] = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
return inverted;
|
|
||||||
}
|
|
99
src/utils.ts
99
src/utils.ts
@ -1,99 +0,0 @@
|
|||||||
import Doc from 'fyo/model/doc';
|
|
||||||
|
|
||||||
export interface QuickEditOptions {
|
|
||||||
schemaName: string;
|
|
||||||
name: string;
|
|
||||||
hideFields?: string[];
|
|
||||||
showFields?: string[];
|
|
||||||
defaults?: Record<string, unknown>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function openQuickEdit({
|
|
||||||
schemaName,
|
|
||||||
name,
|
|
||||||
hideFields,
|
|
||||||
showFields,
|
|
||||||
defaults = {},
|
|
||||||
}: QuickEditOptions) {
|
|
||||||
const router = (await import('./router')).default;
|
|
||||||
|
|
||||||
const currentRoute = router.currentRoute.value;
|
|
||||||
const query = currentRoute.query;
|
|
||||||
let method: 'push' | 'replace' = 'push';
|
|
||||||
|
|
||||||
if (query.edit && query.doctype === schemaName) {
|
|
||||||
// replace the current route if we are
|
|
||||||
// editing another document of the same doctype
|
|
||||||
method = 'replace';
|
|
||||||
}
|
|
||||||
if (query.name === name) return;
|
|
||||||
|
|
||||||
const forWhat = (defaults?.for ?? []) as string[];
|
|
||||||
if (forWhat[0] === 'not in') {
|
|
||||||
const purpose = forWhat[1]?.[0];
|
|
||||||
|
|
||||||
defaults = Object.assign({
|
|
||||||
for:
|
|
||||||
purpose === 'sales'
|
|
||||||
? 'purchases'
|
|
||||||
: purpose === 'purchases'
|
|
||||||
? 'sales'
|
|
||||||
: 'both',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forWhat[0] === 'not in' && forWhat[1] === 'sales') {
|
|
||||||
defaults = Object.assign({ for: 'purchases' });
|
|
||||||
}
|
|
||||||
|
|
||||||
router[method]({
|
|
||||||
query: {
|
|
||||||
edit: 1,
|
|
||||||
doctype: schemaName,
|
|
||||||
name,
|
|
||||||
showFields: showFields ?? getShowFields(schemaName),
|
|
||||||
hideFields,
|
|
||||||
valueJSON: stringifyCircular(defaults),
|
|
||||||
// @ts-ignore
|
|
||||||
lastRoute: currentRoute,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getShowFields(schemaName: string) {
|
|
||||||
if (schemaName === 'Party') {
|
|
||||||
return ['customer'];
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function stringifyCircular(
|
|
||||||
obj: Record<string, unknown>,
|
|
||||||
ignoreCircular = false,
|
|
||||||
convertDocument = false
|
|
||||||
) {
|
|
||||||
const cacheKey: string[] = [];
|
|
||||||
const cacheValue: unknown[] = [];
|
|
||||||
|
|
||||||
return JSON.stringify(obj, (key, value) => {
|
|
||||||
if (typeof value !== 'object' || value === null) {
|
|
||||||
cacheKey.push(key);
|
|
||||||
cacheValue.push(value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cacheValue.includes(value)) {
|
|
||||||
const circularKey = cacheKey[cacheValue.indexOf(value)] || '{self}';
|
|
||||||
return ignoreCircular ? undefined : `[Circular:${circularKey}]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheKey.push(key);
|
|
||||||
cacheValue.push(value);
|
|
||||||
|
|
||||||
if (convertDocument && value instanceof Doc) {
|
|
||||||
return value.getValidDict();
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
}
|
|
@ -17,26 +17,26 @@ export const statusColor = {
|
|||||||
Cancelled: 'red',
|
Cancelled: 'red',
|
||||||
};
|
};
|
||||||
|
|
||||||
const getValidColor = (color) => {
|
const getValidColor = (color: string) => {
|
||||||
const isValid = ['gray', 'orange', 'green', 'red', 'yellow', 'blue'].includes(
|
const isValid = ['gray', 'orange', 'green', 'red', 'yellow', 'blue'].includes(
|
||||||
color
|
color
|
||||||
);
|
);
|
||||||
return isValid ? color : 'gray';
|
return isValid ? color : 'gray';
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getBgColorClass(color) {
|
export function getBgColorClass(color: string) {
|
||||||
return `bg-${getValidColor(color)}-100`;
|
return `bg-${getValidColor(color)}-100`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getColorClass(color, type, value = 300) {
|
export function getColorClass(color: string, type: 'bg' | 'text', value = 300) {
|
||||||
return `${type}-${getValidColor(color)}-${value}`;
|
return `${type}-${getValidColor(color)}-${value}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTextColorClass(color) {
|
export function getTextColorClass(color: string) {
|
||||||
return `text-${getValidColor(color)}-600`;
|
return `text-${getValidColor(color)}-600`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBgTextColorClass(color) {
|
export function getBgTextColorClass(color: string) {
|
||||||
const bg = getBgColorClass(color);
|
const bg = getBgColorClass(color);
|
||||||
const text = getTextColorClass(color);
|
const text = getTextColorClass(color);
|
||||||
return [bg, text].join(' ');
|
return [bg, text].join(' ');
|
97
src/utils/index.ts
Normal file
97
src/utils/index.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/**
|
||||||
|
* General purpose utils used by the frontend.
|
||||||
|
*/
|
||||||
|
import Doc from 'fyo/model/doc';
|
||||||
|
import { isPesa } from 'fyo/utils';
|
||||||
|
import Money from 'pesa/dist/types/src/money';
|
||||||
|
import { fyo } from 'src/initFyo';
|
||||||
|
|
||||||
|
export function stringifyCircular(
|
||||||
|
obj: unknown,
|
||||||
|
ignoreCircular: boolean = false,
|
||||||
|
convertDocument: boolean = false
|
||||||
|
) {
|
||||||
|
const cacheKey: string[] = [];
|
||||||
|
const cacheValue: unknown[] = [];
|
||||||
|
|
||||||
|
return JSON.stringify(obj, (key, value) => {
|
||||||
|
if (typeof value !== 'object' || value === null) {
|
||||||
|
cacheKey.push(key);
|
||||||
|
cacheValue.push(value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cacheValue.includes(value)) {
|
||||||
|
const circularKey = cacheKey[cacheValue.indexOf(value)] || '{self}';
|
||||||
|
return ignoreCircular ? undefined : `[Circular:${circularKey}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheKey.push(key);
|
||||||
|
cacheValue.push(value);
|
||||||
|
|
||||||
|
if (convertDocument && value instanceof Doc) {
|
||||||
|
return value.getValidDict();
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fuzzyMatch(keyword: string, candidate: string) {
|
||||||
|
const keywordLetters = [...keyword];
|
||||||
|
const candidateLetters = [...candidate];
|
||||||
|
|
||||||
|
let keywordLetter = keywordLetters.shift();
|
||||||
|
let candidateLetter = candidateLetters.shift();
|
||||||
|
|
||||||
|
let isMatch = true;
|
||||||
|
let distance = 0;
|
||||||
|
|
||||||
|
while (keywordLetter && candidateLetter) {
|
||||||
|
if (keywordLetter.toLowerCase() === candidateLetter.toLowerCase()) {
|
||||||
|
keywordLetter = keywordLetters.shift();
|
||||||
|
} else {
|
||||||
|
distance += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
candidateLetter = candidateLetters.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keywordLetter !== undefined) {
|
||||||
|
distance = -1;
|
||||||
|
isMatch = false;
|
||||||
|
} else {
|
||||||
|
distance += candidateLetters.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { isMatch, distance };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatXLabels(label: string) {
|
||||||
|
// Format: Mmm YYYY -> Mm YY
|
||||||
|
const splits = label.split(' ');
|
||||||
|
const month = splits[0];
|
||||||
|
const year = splits[1].slice(2);
|
||||||
|
|
||||||
|
return `${month} ${year}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertPesaValuesToFloat(obj: Record<string, unknown>) {
|
||||||
|
Object.keys(obj).forEach((key) => {
|
||||||
|
const value = obj[key];
|
||||||
|
if (!isPesa(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj[key] = (value as Money).float;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getIsSetupComplete() {
|
||||||
|
try {
|
||||||
|
const { setupComplete } = await fyo.doc.getSingle('AccountingSettings');
|
||||||
|
return !!setupComplete;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
72
src/utils/ipcCalls.ts
Normal file
72
src/utils/ipcCalls.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* Utils that make ipcRenderer calls.
|
||||||
|
*/
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
import { t } from 'fyo';
|
||||||
|
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
|
||||||
|
import { setLanguageMap } from './language';
|
||||||
|
import { WindowAction } from './types';
|
||||||
|
import { showToast } from './ui';
|
||||||
|
|
||||||
|
export async function checkForUpdates(force = false) {
|
||||||
|
ipcRenderer.invoke(IPC_ACTIONS.CHECK_FOR_UPDATES, force);
|
||||||
|
await setLanguageMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveData(data: string, savePath: string) {
|
||||||
|
await ipcRenderer.invoke(IPC_ACTIONS.SAVE_DATA, data, savePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function showItemInFolder(filePath: string) {
|
||||||
|
await ipcRenderer.send(IPC_MESSAGES.SHOW_ITEM_IN_FOLDER, filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function makePDF(html: string, savePath: string) {
|
||||||
|
await ipcRenderer.invoke(IPC_ACTIONS.SAVE_HTML_AS_PDF, html, savePath);
|
||||||
|
showExportInFolder(t`Save as PDF Successful`, savePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function runWindowAction(name: WindowAction) {
|
||||||
|
switch (name) {
|
||||||
|
case 'close':
|
||||||
|
ipcRenderer.send(IPC_MESSAGES.CLOSE_CURRENT_WINDOW);
|
||||||
|
break;
|
||||||
|
case 'minimize':
|
||||||
|
ipcRenderer.send(IPC_MESSAGES.MINIMIZE_CURRENT_WINDOW);
|
||||||
|
break;
|
||||||
|
case 'maximize':
|
||||||
|
const maximizing = await ipcRenderer.invoke(
|
||||||
|
IPC_ACTIONS.TOGGLE_MAXIMIZE_CURRENT_WINDOW
|
||||||
|
);
|
||||||
|
name = maximizing ? name : 'unmaximize';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showExportInFolder(message: string, filePath: string) {
|
||||||
|
showToast({
|
||||||
|
message,
|
||||||
|
actionText: t`Open Folder`,
|
||||||
|
type: 'success',
|
||||||
|
action: async () => {
|
||||||
|
await 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 = filePath + extention;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { canceled, filePath };
|
||||||
|
}
|
71
src/utils/language.ts
Normal file
71
src/utils/language.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
import { DEFAULT_LANGUAGE } from 'fyo/utils/consts';
|
||||||
|
import { setLanguageMapOnTranslationString } from 'fyo/utils/translation';
|
||||||
|
import { fyo } from 'src/initFyo';
|
||||||
|
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
|
||||||
|
import { showToast } from './ui';
|
||||||
|
|
||||||
|
// Language: Language Code in books/translations
|
||||||
|
export const languageCodeMap: Record<string, string> = {
|
||||||
|
English: 'en',
|
||||||
|
French: 'fr',
|
||||||
|
German: 'de',
|
||||||
|
Portuguese: 'pt',
|
||||||
|
Arabic: 'ar',
|
||||||
|
Catalan: 'ca-ES',
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function setLanguageMap(
|
||||||
|
initLanguage?: string,
|
||||||
|
dontReload: boolean = false
|
||||||
|
) {
|
||||||
|
const oldLanguage = fyo.config.get('language') as string;
|
||||||
|
initLanguage ??= oldLanguage;
|
||||||
|
const { code, language, usingDefault } = getLanguageCode(
|
||||||
|
initLanguage,
|
||||||
|
oldLanguage
|
||||||
|
);
|
||||||
|
|
||||||
|
let success = true;
|
||||||
|
if (code === 'en') {
|
||||||
|
setLanguageMapOnTranslationString(undefined);
|
||||||
|
} else {
|
||||||
|
success = await fetchAndSetLanguageMap(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success && !usingDefault) {
|
||||||
|
fyo.config.set('language', language);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dontReload && success && initLanguage !== oldLanguage) {
|
||||||
|
await ipcRenderer.send(IPC_MESSAGES.RELOAD_MAIN_WINDOW);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLanguageCode(initLanguage: string, oldLanguage: string) {
|
||||||
|
let language = initLanguage ?? oldLanguage;
|
||||||
|
let usingDefault = false;
|
||||||
|
|
||||||
|
if (!language) {
|
||||||
|
language = DEFAULT_LANGUAGE;
|
||||||
|
usingDefault = true;
|
||||||
|
}
|
||||||
|
const code = languageCodeMap[language] ?? 'en';
|
||||||
|
return { code, language, usingDefault };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchAndSetLanguageMap(code: string) {
|
||||||
|
const { success, message, languageMap } = await ipcRenderer.invoke(
|
||||||
|
IPC_ACTIONS.GET_LANGUAGE_MAP,
|
||||||
|
code
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
showToast({ type: 'error', message });
|
||||||
|
} else {
|
||||||
|
setLanguageMapOnTranslationString(languageMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
29
src/utils/types.ts
Normal file
29
src/utils/types.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
export interface MessageDialogButton {
|
||||||
|
label: string;
|
||||||
|
action: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MessageDialogOptions {
|
||||||
|
message: string;
|
||||||
|
detail?: string;
|
||||||
|
buttons?: MessageDialogButton[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ToastOptions {
|
||||||
|
message: string;
|
||||||
|
type?: 'info' | 'warning' | 'error' | 'success';
|
||||||
|
duration?: number;
|
||||||
|
action?: () => void;
|
||||||
|
actionText?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WindowAction = 'close' | 'minimize' | 'maximize' | 'unmaximize';
|
||||||
|
export type SettingsTab = 'Invoice' | 'General' | 'System';
|
||||||
|
|
||||||
|
export interface QuickEditOptions {
|
||||||
|
schemaName: string;
|
||||||
|
name: string;
|
||||||
|
hideFields?: string[];
|
||||||
|
showFields?: string[];
|
||||||
|
defaults?: Record<string, unknown>;
|
||||||
|
}
|
329
src/utils/ui.ts
Normal file
329
src/utils/ui.ts
Normal file
@ -0,0 +1,329 @@
|
|||||||
|
/**
|
||||||
|
* Utils to do UI stuff such as opening dialogs, toasts, etc.
|
||||||
|
* Basically anything that may directly or indirectly import a Vue file.
|
||||||
|
*/
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
import { t } from 'fyo';
|
||||||
|
import Doc from 'fyo/model/doc';
|
||||||
|
import { Action } from 'fyo/model/types';
|
||||||
|
import { getActions } from 'fyo/utils';
|
||||||
|
import { handleErrorWithDialog } from 'src/errorHandling';
|
||||||
|
import { fyo } from 'src/initFyo';
|
||||||
|
import router from 'src/router';
|
||||||
|
import { IPC_ACTIONS } from 'utils/messages';
|
||||||
|
import { App, createApp, h } from 'vue';
|
||||||
|
import { RouteLocationRaw } from 'vue-router';
|
||||||
|
import { stringifyCircular } from './';
|
||||||
|
import {
|
||||||
|
MessageDialogOptions,
|
||||||
|
QuickEditOptions,
|
||||||
|
SettingsTab,
|
||||||
|
ToastOptions,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
export async function openQuickEdit({
|
||||||
|
schemaName,
|
||||||
|
name,
|
||||||
|
hideFields,
|
||||||
|
showFields,
|
||||||
|
defaults = {},
|
||||||
|
}: QuickEditOptions) {
|
||||||
|
const router = (await import('src/router')).default;
|
||||||
|
|
||||||
|
const currentRoute = router.currentRoute.value;
|
||||||
|
const query = currentRoute.query;
|
||||||
|
let method: 'push' | 'replace' = 'push';
|
||||||
|
|
||||||
|
if (query.edit && query.doctype === schemaName) {
|
||||||
|
// replace the current route if we are
|
||||||
|
// editing another document of the same doctype
|
||||||
|
method = 'replace';
|
||||||
|
}
|
||||||
|
if (query.name === name) return;
|
||||||
|
|
||||||
|
const forWhat = (defaults?.for ?? []) as string[];
|
||||||
|
if (forWhat[0] === 'not in') {
|
||||||
|
const purpose = forWhat[1]?.[0];
|
||||||
|
|
||||||
|
defaults = Object.assign({
|
||||||
|
for:
|
||||||
|
purpose === 'sales'
|
||||||
|
? 'purchases'
|
||||||
|
: purpose === 'purchases'
|
||||||
|
? 'sales'
|
||||||
|
: 'both',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forWhat[0] === 'not in' && forWhat[1] === 'sales') {
|
||||||
|
defaults = Object.assign({ for: 'purchases' });
|
||||||
|
}
|
||||||
|
|
||||||
|
router[method]({
|
||||||
|
query: {
|
||||||
|
edit: 1,
|
||||||
|
doctype: schemaName,
|
||||||
|
name,
|
||||||
|
showFields: showFields ?? getShowFields(schemaName),
|
||||||
|
hideFields,
|
||||||
|
valueJSON: stringifyCircular(defaults),
|
||||||
|
// @ts-ignore
|
||||||
|
lastRoute: currentRoute,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getShowFields(schemaName: string) {
|
||||||
|
if (schemaName === 'Party') {
|
||||||
|
return ['customer'];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function showMessageDialog({
|
||||||
|
message,
|
||||||
|
detail,
|
||||||
|
buttons = [],
|
||||||
|
}: MessageDialogOptions) {
|
||||||
|
const options = {
|
||||||
|
message,
|
||||||
|
detail,
|
||||||
|
buttons: buttons.map((a) => a.label),
|
||||||
|
};
|
||||||
|
|
||||||
|
const { response } = (await ipcRenderer.invoke(
|
||||||
|
IPC_ACTIONS.GET_DIALOG_RESPONSE,
|
||||||
|
options
|
||||||
|
)) as { response: number };
|
||||||
|
|
||||||
|
const button = buttons[response];
|
||||||
|
if (button && button.action) {
|
||||||
|
button.action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function showToast(options: ToastOptions) {
|
||||||
|
const Toast = (await import('src/components/Toast.vue')).default;
|
||||||
|
const toast = createApp({
|
||||||
|
render() {
|
||||||
|
return h(Toast, { ...options });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
replaceAndAppendMount(toast, 'toast-target');
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceAndAppendMount(app: App<Element>, replaceId: string) {
|
||||||
|
const fragment = document.createDocumentFragment();
|
||||||
|
const target = document.getElementById(replaceId);
|
||||||
|
if (target === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent = target.parentElement;
|
||||||
|
const clone = target.cloneNode();
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
app.mount(fragment);
|
||||||
|
target.replaceWith(fragment);
|
||||||
|
parent!.append(clone);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function openSettings(tab: SettingsTab) {
|
||||||
|
routeTo({ path: '/settings', query: { tab } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function routeTo(route: string | RouteLocationRaw) {
|
||||||
|
let routeOptions = route;
|
||||||
|
if (
|
||||||
|
typeof route === 'string' &&
|
||||||
|
route === router.currentRoute.value.fullPath
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof route === 'string') {
|
||||||
|
routeOptions = { path: route };
|
||||||
|
}
|
||||||
|
|
||||||
|
router.push(routeOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteDocWithPrompt(doc: Doc) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
showMessageDialog({
|
||||||
|
message: t`Are you sure you want to delete ${
|
||||||
|
doc.schemaName
|
||||||
|
} ${doc.name!}?`,
|
||||||
|
detail: t`This action is permanent`,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: t`Delete`,
|
||||||
|
action: () => {
|
||||||
|
doc
|
||||||
|
.delete()
|
||||||
|
.then(() => resolve(true))
|
||||||
|
.catch((e: Error) => {
|
||||||
|
handleErrorWithDialog(e, doc);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t`Cancel`,
|
||||||
|
action() {
|
||||||
|
resolve(false);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function cancelDocWithPrompt(doc: Doc) {
|
||||||
|
let detail = t`This action is permanent`;
|
||||||
|
if (['SalesInvoice', 'PurchaseInvoice'].includes(doc.schemaName)) {
|
||||||
|
const payments = (
|
||||||
|
await fyo.db.getAll('Payment', {
|
||||||
|
fields: ['name'],
|
||||||
|
filters: { cancelled: false },
|
||||||
|
})
|
||||||
|
).map(({ name }) => name);
|
||||||
|
|
||||||
|
const query = (
|
||||||
|
await fyo.db.getAll('PaymentFor', {
|
||||||
|
fields: ['parent'],
|
||||||
|
filters: {
|
||||||
|
referenceName: doc.name!,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).filter(({ parent }) => payments.includes(parent));
|
||||||
|
|
||||||
|
const paymentList = [...new Set(query.map(({ parent }) => parent))];
|
||||||
|
|
||||||
|
if (paymentList.length === 1) {
|
||||||
|
detail = t`This action is permanent and will cancel the following payment: ${
|
||||||
|
paymentList[0] as string
|
||||||
|
}`;
|
||||||
|
} else if (paymentList.length > 1) {
|
||||||
|
detail = t`This action is permanent and will cancel the following payments: ${paymentList.join(
|
||||||
|
', '
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
showMessageDialog({
|
||||||
|
message: t`Are you sure you want to cancel ${
|
||||||
|
doc.schemaName
|
||||||
|
} ${doc.name!}?`,
|
||||||
|
detail,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: t`Yes`,
|
||||||
|
async action() {
|
||||||
|
const entryDoc = await fyo.doc.getDoc(doc.schemaName, doc.name!);
|
||||||
|
entryDoc.cancelled = 1;
|
||||||
|
await entryDoc.update();
|
||||||
|
entryDoc
|
||||||
|
.revert()
|
||||||
|
.then(() => resolve(true))
|
||||||
|
.catch((e) => {
|
||||||
|
handleErrorWithDialog(e, doc);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t`No`,
|
||||||
|
action() {
|
||||||
|
resolve(false);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getActionsForDocument(doc?: Doc): Action[] {
|
||||||
|
if (!doc) return [];
|
||||||
|
|
||||||
|
const actions: Action[] = [
|
||||||
|
...getActions(doc, fyo),
|
||||||
|
getDuplicateAction(doc),
|
||||||
|
getDeleteAction(doc),
|
||||||
|
getCancelAction(doc),
|
||||||
|
];
|
||||||
|
|
||||||
|
return actions
|
||||||
|
.filter((d) => d.condition?.(doc) ?? true)
|
||||||
|
.map((d) => {
|
||||||
|
return {
|
||||||
|
label: d.label,
|
||||||
|
component: d.component,
|
||||||
|
action: d.action,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCancelAction(doc: Doc): Action {
|
||||||
|
return {
|
||||||
|
label: t`Cancel`,
|
||||||
|
component: {
|
||||||
|
template: '<span class="text-red-700">{{ t`Cancel` }}</span>',
|
||||||
|
},
|
||||||
|
condition: (doc: Doc) => !!(doc.submitted && !doc.cancelled),
|
||||||
|
action: () => {
|
||||||
|
cancelDocWithPrompt(doc).then((res) => {
|
||||||
|
if (res) {
|
||||||
|
router.push(`/list/${doc.schemaName}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDeleteAction(doc: Doc): Action {
|
||||||
|
return {
|
||||||
|
label: t`Delete`,
|
||||||
|
component: {
|
||||||
|
template: '<span class="text-red-700">{{ t`Delete` }}</span>',
|
||||||
|
},
|
||||||
|
condition: (doc: Doc) =>
|
||||||
|
!doc.isNew && !doc.submitted && !doc.schema.isSingle && !doc.cancelled,
|
||||||
|
action: () =>
|
||||||
|
deleteDocWithPrompt(doc).then((res) => {
|
||||||
|
if (res) {
|
||||||
|
routeTo(`/list/${doc.schemaName}`);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function getDuplicateAction(doc: Doc): Action {
|
||||||
|
const isSubmittable = !!doc.schema.isSubmittable;
|
||||||
|
return {
|
||||||
|
label: t`Duplicate`,
|
||||||
|
condition: (doc: Doc) =>
|
||||||
|
!!(
|
||||||
|
((isSubmittable && doc && doc.submitted) || !isSubmittable) &&
|
||||||
|
!doc._notInserted &&
|
||||||
|
!(doc.cancelled || false)
|
||||||
|
),
|
||||||
|
action: () => {
|
||||||
|
showMessageDialog({
|
||||||
|
message: t`Duplicate ${doc.schemaName} ${doc.name!}?`,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
label: t`Yes`,
|
||||||
|
async action() {
|
||||||
|
doc.duplicate();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t`No`,
|
||||||
|
action() {
|
||||||
|
// no-op
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
@ -15,10 +15,11 @@
|
|||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"types": ["webpack-env"],
|
"types": ["webpack-env"],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["src/*"],
|
"src/*": ["src/*"],
|
||||||
"schemas/*": ["schemas/*"],
|
"schemas/*": ["schemas/*"],
|
||||||
"backend/*": ["backend/*"],
|
"backend/*": ["backend/*"],
|
||||||
"regional/*": ["regional/*"],
|
"regional/*": ["regional/*"],
|
||||||
|
"fixtures/*": ["fixtures/*"],
|
||||||
"utils/*": ["utils/*"]
|
"utils/*": ["utils/*"]
|
||||||
},
|
},
|
||||||
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
|
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
|
||||||
@ -43,7 +44,8 @@
|
|||||||
"accounting/**/*.ts",
|
"accounting/**/*.ts",
|
||||||
|
|
||||||
"scripts/**/*.ts",
|
"scripts/**/*.ts",
|
||||||
"utils/csvParser.ts"
|
"utils/csvParser.ts",
|
||||||
, "utils/config.ts" ],
|
"utils/config.ts"
|
||||||
|
],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
@ -68,3 +68,14 @@ export function titleCase(phrase: string): string {
|
|||||||
})
|
})
|
||||||
.join(' ');
|
.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function invertMap(map: Record<string, string>): Record<string, string> {
|
||||||
|
const keys = Object.keys(map);
|
||||||
|
const inverted: Record<string, string> = {};
|
||||||
|
for (const key of keys) {
|
||||||
|
const val = map[key];
|
||||||
|
inverted[val] = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return inverted;
|
||||||
|
}
|
||||||
|
@ -40,11 +40,12 @@ module.exports = {
|
|||||||
configureWebpack(config) {
|
configureWebpack(config) {
|
||||||
Object.assign(config.resolve.alias, {
|
Object.assign(config.resolve.alias, {
|
||||||
fyo: path.resolve(__dirname, './fyo'),
|
fyo: path.resolve(__dirname, './fyo'),
|
||||||
'~': path.resolve('.'),
|
src: path.resolve(__dirname, './src'),
|
||||||
schemas: path.resolve(__dirname, './schemas'),
|
schemas: path.resolve(__dirname, './schemas'),
|
||||||
backend: path.resolve(__dirname, './backend'),
|
backend: path.resolve(__dirname, './backend'),
|
||||||
utils: path.resolve(__dirname, './utils'),
|
utils: path.resolve(__dirname, './utils'),
|
||||||
regional: path.resolve(__dirname, './regional'),
|
regional: path.resolve(__dirname, './regional'),
|
||||||
|
fixtures: path.resolve(__dirname, './fixtures'),
|
||||||
});
|
});
|
||||||
|
|
||||||
config.plugins.push(
|
config.plugins.push(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user