mirror of
https://github.com/frappe/books.git
synced 2024-11-09 23:30:56 +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 { DateTime } from 'luxon';
|
||||
import { showMessageDialog } from 'src/utils';
|
||||
import { stateCodeMap } from '../regional/in';
|
||||
import { exportCsv, saveExportData } from '../reports/commonExporter';
|
||||
import { getSavePath } from '../src/utils';
|
||||
@ -50,7 +50,7 @@ export async function generateGstr1Json(getReportData) {
|
||||
if (!gstin) {
|
||||
showMessageDialog({
|
||||
message: t`Export Failed`,
|
||||
description: t`Please set GSTIN in General Settings.`,
|
||||
detail: t`Please set GSTIN in General Settings.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -232,7 +232,7 @@ export async function generateGstr2Csv(getReportData) {
|
||||
if (!gstin) {
|
||||
showMessageDialog({
|
||||
message: t`Export Failed`,
|
||||
description: t`Please set GSTIN in General Settings.`,
|
||||
detail: t`Please set GSTIN in General Settings.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -313,7 +313,7 @@ export async function generateGstr1Csv(getReportData) {
|
||||
if (!gstin) {
|
||||
showMessageDialog({
|
||||
message: t`Export Failed`,
|
||||
description: t`Please set GSTIN in General Settings.`,
|
||||
detail: t`Please set GSTIN in General Settings.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -59,8 +59,11 @@ export type ListsMap = Record<string, ListFunction>;
|
||||
|
||||
export interface Action {
|
||||
label: string;
|
||||
condition: (doc: Doc) => boolean;
|
||||
action: (doc: Doc, router: Router) => Promise<void>;
|
||||
action: (doc: Doc, router: Router) => Promise<void> | void;
|
||||
condition?: (doc: Doc) => boolean;
|
||||
component?: {
|
||||
template?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ColumnConfig {
|
||||
|
@ -41,7 +41,7 @@ describe('Fyo Init', function () {
|
||||
describe('Fyo Docs', function () {
|
||||
const countryCode = 'in';
|
||||
let fyo: Fyo;
|
||||
const schemas = getSchemas(countryCode);
|
||||
const schemaMap = getSchemas(countryCode);
|
||||
this.beforeEach(async function () {
|
||||
fyo = new Fyo({
|
||||
DatabaseDemux: DatabaseManager,
|
||||
@ -58,7 +58,14 @@ describe('Fyo Docs', function () {
|
||||
await fyo.close();
|
||||
});
|
||||
|
||||
specify('temp', async function () {
|
||||
fyo.db.schemaMap;
|
||||
specify('getEmptyDoc', async function () {
|
||||
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';
|
||||
|
||||
export function slug(str: string) {
|
||||
@ -50,3 +53,12 @@ export function getDuplicates(array: unknown[]) {
|
||||
export function isPesa(value: unknown): boolean {
|
||||
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;
|
||||
}
|
||||
|
||||
export function setLanguageMapOnTranslationString(languageMap: LanguageMap) {
|
||||
export function setLanguageMapOnTranslationString(
|
||||
languageMap: LanguageMap | undefined
|
||||
) {
|
||||
TranslationString.prototype.languageMap = languageMap;
|
||||
}
|
||||
|
@ -18,8 +18,12 @@ export abstract class Invoice extends Doc {
|
||||
currency?: string;
|
||||
netTotal?: Money;
|
||||
baseGrandTotal?: Money;
|
||||
outstandingAmount?: Money;
|
||||
exchangeRate?: number;
|
||||
|
||||
submitted?: boolean;
|
||||
cancelled?: boolean;
|
||||
|
||||
abstract getPosting(): Promise<LedgerPosting>;
|
||||
|
||||
get isSales() {
|
||||
|
@ -43,7 +43,7 @@ export function getTransactionActions(schemaName: string, fyo: Fyo): Action[] {
|
||||
const paymentType = isSales ? 'Receive' : 'Pay';
|
||||
const hideAccountField = isSales ? 'account' : 'paymentAccount';
|
||||
|
||||
const { openQuickEdit } = await import('../src/utils');
|
||||
const { openQuickEdit } = await import('src/utils/ui');
|
||||
await openQuickEdit({
|
||||
schemaName: 'Payment',
|
||||
name: payment.name as string,
|
||||
|
@ -1,7 +1,33 @@
|
||||
import { partyWithAvatar } from '@/utils';
|
||||
import { t } from 'fyo';
|
||||
import Avatar from 'src/components/Avatar.vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
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`;
|
||||
|
||||
const viewConfig = {
|
||||
@ -117,7 +143,7 @@ const viewConfig = {
|
||||
fieldtype: 'Link',
|
||||
fieldname: 'party',
|
||||
component(cellValue) {
|
||||
return partyWithAvatar(cellValue);
|
||||
return getPartyWithAvatar(cellValue);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
12
src/App.vue
12
src/App.vue
@ -30,16 +30,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WindowsTitleBar from '@/components/WindowsTitleBar';
|
||||
import config from '@/config';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import fs from 'fs/promises';
|
||||
import frappe from 'fyo';
|
||||
import WindowsTitleBar from 'src/components/WindowsTitleBar';
|
||||
import config from 'src/config';
|
||||
import {
|
||||
connectToLocalDatabase,
|
||||
postSetup,
|
||||
purgeCache
|
||||
} from '@/initialization';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import fs from 'fs/promises';
|
||||
import frappe from 'fyo';
|
||||
} from 'src/initialization';
|
||||
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
|
||||
import TelemetryModal from './components/once/TelemetryModal.vue';
|
||||
import { showErrorDialog } from './errorHandling';
|
||||
|
@ -32,9 +32,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dropdown from 'src/components/Dropdown';
|
||||
import { fuzzyMatch } from 'src/utils';
|
||||
import Base from './Base';
|
||||
import Dropdown from '@/components/Dropdown';
|
||||
import { fuzzyMatch } from '@/utils';
|
||||
|
||||
export default {
|
||||
name: 'AutoComplete',
|
||||
|
@ -55,9 +55,9 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Popover from 'src/components/Popover';
|
||||
import Row from 'src/components/Row';
|
||||
import Base from './Base';
|
||||
import Row from '@/components/Row';
|
||||
import Popover from '@/components/Popover';
|
||||
|
||||
export default {
|
||||
name: 'Color',
|
||||
|
@ -7,10 +7,10 @@
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
import config from '@/config';
|
||||
import { languageCodeMap } from '@/languageCodeMap';
|
||||
import { setLanguageMap } from '@/utils';
|
||||
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';
|
||||
|
||||
export default {
|
||||
|
@ -1,12 +1,12 @@
|
||||
import Badge from '@/components/Badge';
|
||||
import { openQuickEdit } from '@/utils';
|
||||
import Badge from 'src/components/Badge';
|
||||
import { openQuickEdit } from 'src/utils';
|
||||
import frappe, { t } from 'frappe';
|
||||
import { markRaw } from 'vue';
|
||||
import AutoComplete from './AutoComplete';
|
||||
<script>
|
||||
import Badge from '@/components/Badge';
|
||||
import { openQuickEdit } from '@/utils';
|
||||
import frappe, { t } from 'frappe';
|
||||
import Badge from 'src/components/Badge';
|
||||
import { openQuickEdit } from 'src/utils';
|
||||
import { markRaw } from 'vue';
|
||||
import AutoComplete from './AutoComplete';
|
||||
|
||||
|
@ -62,7 +62,7 @@
|
||||
|
||||
<script>
|
||||
import frappe from 'frappe';
|
||||
import Row from '@/components/Row';
|
||||
import Row from 'src/components/Row';
|
||||
import Base from './Base';
|
||||
import TableRow from './TableRow';
|
||||
|
||||
|
@ -32,9 +32,9 @@
|
||||
</Row>
|
||||
</template>
|
||||
<script>
|
||||
import FormControl from './FormControl';
|
||||
import Row from '@/components/Row';
|
||||
import Row from 'src/components/Row';
|
||||
import { getErrorMessage } from '../../errorHandling';
|
||||
import FormControl from './FormControl';
|
||||
|
||||
export default {
|
||||
name: 'TableRow',
|
||||
|
@ -21,8 +21,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dropdown from '@/components/Dropdown';
|
||||
import Button from '@/components/Button';
|
||||
import Button from 'src/components/Button';
|
||||
import Dropdown from 'src/components/Dropdown';
|
||||
|
||||
export default {
|
||||
name: 'DropdownWithActions',
|
||||
|
@ -47,10 +47,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import InvoiceTemplate1 from '@/../models/doctype/SalesInvoice/Templates/InvoiceTemplate1';
|
||||
import InvoiceTemplate2 from '@/../models/doctype/SalesInvoice/Templates/InvoiceTemplate2';
|
||||
import InvoiceTemplate3 from '@/../models/doctype/SalesInvoice/Templates/InvoiceTemplate3';
|
||||
import InvoiceCustomizer from '@/components/InvoiceCustomizer';
|
||||
import InvoiceTemplate1 from 'src/../models/doctype/SalesInvoice/Templates/InvoiceTemplate1';
|
||||
import InvoiceTemplate2 from 'src/../models/doctype/SalesInvoice/Templates/InvoiceTemplate2';
|
||||
import InvoiceTemplate3 from 'src/../models/doctype/SalesInvoice/Templates/InvoiceTemplate3';
|
||||
import InvoiceCustomizer from 'src/components/InvoiceCustomizer';
|
||||
|
||||
const invoiceTemplates = {
|
||||
'Basic I': InvoiceTemplate1,
|
||||
|
@ -41,9 +41,9 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Dropdown from '@/components/Dropdown';
|
||||
import { routeTo } from '@/utils';
|
||||
import frappe, { t } from 'frappe';
|
||||
import Dropdown from 'src/components/Dropdown';
|
||||
import { routeTo } from 'src/utils';
|
||||
import reports from '../../reports/view';
|
||||
|
||||
export default {
|
||||
|
@ -96,10 +96,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Button from '@/components/Button';
|
||||
import { reportIssue } from '@/errorHandling';
|
||||
import { routeTo } from '@/utils';
|
||||
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 sidebarConfig from '../sidebarConfig';
|
||||
import Icon from './Icon.vue';
|
||||
|
@ -4,8 +4,8 @@
|
||||
}}</Badge>
|
||||
</template>
|
||||
<script>
|
||||
import Badge from 'src/components/Badge';
|
||||
import { statusColor } from '../colors';
|
||||
import Badge from '@/components/Badge';
|
||||
|
||||
export default {
|
||||
name: 'StatusBadge',
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Button from '@/components/Button';
|
||||
import FormControl from '@/components/Controls/FormControl';
|
||||
import Button from 'src/components/Button';
|
||||
import FormControl from 'src/components/Controls/FormControl';
|
||||
import frappe from 'frappe';
|
||||
import { getErrorMessage, handleErrorWithDialog } from '../errorHandling';
|
||||
<template>
|
||||
@ -90,9 +90,9 @@ import { getErrorMessage, handleErrorWithDialog } from '../errorHandling';
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Button from '@/components/Button';
|
||||
import FormControl from '@/components/Controls/FormControl';
|
||||
import frappe from 'frappe';
|
||||
import Button from 'src/components/Button';
|
||||
import FormControl from 'src/components/Controls/FormControl';
|
||||
import { getErrorMessage, handleErrorWithDialog } from '../errorHandling';
|
||||
|
||||
let TwoColumnForm = {
|
||||
|
@ -49,8 +49,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { routeTo } from '@/utils';
|
||||
import frappe from 'frappe';
|
||||
import { routeTo } from 'src/utils';
|
||||
import { getStatusColumn } from '../Transaction/Transaction';
|
||||
|
||||
export default {
|
||||
|
@ -19,7 +19,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { runWindowAction } from '@/utils';
|
||||
import { runWindowAction } from 'src/utils';
|
||||
|
||||
export default {
|
||||
name: 'WindowControls',
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
<script>
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { runWindowAction } from '@/utils';
|
||||
import { runWindowAction } from 'src/utils';
|
||||
import { IPC_MESSAGES } from 'utils/messages';
|
||||
|
||||
export default {
|
||||
|
@ -49,10 +49,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import config, { ConfigKeys, TelemetrySetting } from '@/config';
|
||||
import { getTelemetryOptions } from '@/telemetry/helpers';
|
||||
import telemetry from '@/telemetry/telemetry';
|
||||
import { NounEnum, Verb } from '@/telemetry/types';
|
||||
import config, { ConfigKeys, TelemetrySetting } from 'src/config';
|
||||
import { getTelemetryOptions } from 'src/telemetry/helpers';
|
||||
import telemetry from 'src/telemetry/telemetry';
|
||||
import { NounEnum, Verb } from 'src/telemetry/types';
|
||||
import Button from '../Button.vue';
|
||||
import FormControl from '../Controls/FormControl';
|
||||
import FeatherIcon from '../FeatherIcon.vue';
|
||||
|
@ -1,11 +1,11 @@
|
||||
import frappe from 'fyo';
|
||||
import { t } from 'fyo';
|
||||
import { DocValueMap } from 'fyo/core/types';
|
||||
import Doc from 'fyo/model/doc';
|
||||
import { isNameAutoSet } from 'fyo/model/naming';
|
||||
import { Noun, Verb } from 'fyo/telemetry/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 { fyo } from './initFyo';
|
||||
|
||||
export const importable = [
|
||||
'SalesInvoice',
|
||||
@ -364,7 +364,7 @@ export class Importer {
|
||||
|
||||
async importData(setLoadingStatus: LoadingStatusCallback): Promise<Status> {
|
||||
const status: Status = { success: false, names: [], message: '' };
|
||||
const shouldDeleteName = isNameAutoSet(this.doctype);
|
||||
const shouldDeleteName = isNameAutoSet(this.doctype, fyo);
|
||||
const docObjs = this.getDocs();
|
||||
|
||||
let entriesMade = 0;
|
||||
@ -383,7 +383,7 @@ export class Importer {
|
||||
delete docObj[key];
|
||||
}
|
||||
|
||||
const doc: Doc = frappe.doc.getEmptyDoc(this.doctype, false);
|
||||
const doc: Doc = fyo.doc.getEmptyDoc(this.doctype, false);
|
||||
try {
|
||||
await this.makeEntry(doc, docObj);
|
||||
entriesMade += 1;
|
||||
@ -391,7 +391,7 @@ export class Importer {
|
||||
} catch (err) {
|
||||
setLoadingStatus(false, entriesMade, docObjs.length);
|
||||
|
||||
telemetry.log(Verb.Imported, this.doctype as Noun, {
|
||||
fyo.telemetry.log(Verb.Imported, this.doctype as Noun, {
|
||||
success: false,
|
||||
count: entriesMade,
|
||||
});
|
||||
@ -405,7 +405,7 @@ export class Importer {
|
||||
setLoadingStatus(false, entriesMade, docObjs.length);
|
||||
status.success = true;
|
||||
|
||||
telemetry.log(Verb.Imported, this.doctype as Noun, {
|
||||
fyo.telemetry.log(Verb.Imported, this.doctype as Noun, {
|
||||
success: true,
|
||||
count: entriesMade,
|
||||
});
|
||||
@ -426,18 +426,18 @@ export class Importer {
|
||||
}
|
||||
|
||||
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;
|
||||
if (message?.includes('UNIQUE constraint failed')) {
|
||||
messages.push(frappe.t`${doc.name!} already exists.`);
|
||||
messages.push(t`${doc.name!} already exists.`);
|
||||
} else if (message) {
|
||||
messages.push(message);
|
||||
}
|
||||
|
||||
if (status.names.length) {
|
||||
messages.push(
|
||||
frappe.t`The following ${
|
||||
t`The following ${
|
||||
status.names.length
|
||||
} entries were created: ${status.names.join(', ')}`
|
||||
);
|
||||
|
@ -1,6 +1,8 @@
|
||||
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 { TelemetrySetting } from 'fyo/telemetry/types';
|
||||
import {
|
||||
DuplicateEntryError,
|
||||
LinkValidationError,
|
||||
@ -9,12 +11,12 @@ import {
|
||||
} from 'fyo/utils/errors';
|
||||
import { ErrorLog } from 'fyo/utils/types';
|
||||
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
|
||||
import telemetry from '../frappe/telemetry/telemetry';
|
||||
import config, { ConfigKeys, TelemetrySetting } from '../utils/config';
|
||||
import { showMessageDialog, showToast } from './utils.js';
|
||||
import { fyo } from './initFyo';
|
||||
import { ToastOptions } from './utils/types';
|
||||
import { showMessageDialog, showToast } from './utils/ui';
|
||||
|
||||
function getCanLog(): boolean {
|
||||
const telemetrySetting = config.get(ConfigKeys.Telemetry);
|
||||
const telemetrySetting = fyo.config.get(ConfigKeys.Telemetry);
|
||||
return telemetrySetting !== TelemetrySetting.dontLogAnything;
|
||||
}
|
||||
|
||||
@ -36,7 +38,7 @@ async function reportError(errorLogObj: ErrorLog, cb?: Function) {
|
||||
more: JSON.stringify(errorLogObj.more ?? {}),
|
||||
};
|
||||
|
||||
if (frappe.store.isDevelopment) {
|
||||
if (fyo.store.isDevelopment) {
|
||||
console.log('errorHandling');
|
||||
console.log(body);
|
||||
}
|
||||
@ -46,7 +48,7 @@ async function reportError(errorLogObj: ErrorLog, cb?: Function) {
|
||||
}
|
||||
|
||||
function getToastProps(errorLogObj: ErrorLog, canLog: boolean, cb?: Function) {
|
||||
const props = {
|
||||
const props: ToastOptions = {
|
||||
message: t`Error: ` + errorLogObj.name,
|
||||
type: 'error',
|
||||
};
|
||||
@ -73,7 +75,7 @@ export function getErrorLogObject(
|
||||
const errorLogObj = { name, stack, message, more };
|
||||
|
||||
// @ts-ignore
|
||||
frappe.errorLog.push(errorLogObj);
|
||||
fyo.errorLog.push(errorLogObj);
|
||||
|
||||
return errorLogObj;
|
||||
}
|
||||
@ -84,7 +86,7 @@ export function handleError(
|
||||
more?: Record<string, unknown>,
|
||||
cb?: Function
|
||||
) {
|
||||
telemetry.error(error.name);
|
||||
fyo.telemetry.error(error.name);
|
||||
if (shouldLog) {
|
||||
console.error(error);
|
||||
}
|
||||
@ -124,7 +126,7 @@ export function handleErrorWithDialog(error: Error, doc?: Doc) {
|
||||
const errorMessage = getErrorMessage(error, doc);
|
||||
handleError(false, error, { errorMessage, doc });
|
||||
|
||||
showMessageDialog({ message: error.name, description: errorMessage });
|
||||
showMessageDialog({ message: error.name, detail: errorMessage });
|
||||
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 frappe from 'frappe';
|
||||
import { createApp } from 'vue';
|
||||
import App from './App';
|
||||
import FeatherIcon from './components/FeatherIcon';
|
||||
import config, { ConfigKeys } from './config';
|
||||
import { getErrorHandled, handleError } from './errorHandling';
|
||||
import { fyo } from './initFyo';
|
||||
import { IPC_ACTIONS } from './messages';
|
||||
import { incrementOpenCount } from './renderer/helpers';
|
||||
import registerIpcRendererListeners from './renderer/registerIpcRendererListeners';
|
||||
@ -13,7 +13,7 @@ import { outsideClickDirective } from './ui';
|
||||
import { setLanguageMap, stringifyCircular } from './utils';
|
||||
|
||||
(async () => {
|
||||
const language = config.get(ConfigKeys.Language);
|
||||
const language = fyo.config.get(ConfigKeys.Language);
|
||||
if (language) {
|
||||
await setLanguageMap(language);
|
||||
}
|
||||
@ -22,15 +22,15 @@ import { setLanguageMap, stringifyCircular } from './utils';
|
||||
window.config = config;
|
||||
}
|
||||
|
||||
frappe.isElectron = true;
|
||||
fyo.isElectron = true;
|
||||
|
||||
const models = (await import('../models')).default;
|
||||
await frappe.initializeAndRegister(models);
|
||||
await fyo.initializeAndRegister(models);
|
||||
|
||||
ipcRenderer.send = getErrorHandled(ipcRenderer.send);
|
||||
ipcRenderer.invoke = getErrorHandled(ipcRenderer.invoke);
|
||||
|
||||
window.frappe = frappe;
|
||||
window.frappe = fyo;
|
||||
|
||||
window.onerror = (message, source, lineno, colno, error) => {
|
||||
error = error ?? new Error('triggered in window.onerror');
|
||||
@ -50,7 +50,7 @@ import { setLanguageMap, stringifyCircular } from './utils';
|
||||
app.mixin({
|
||||
computed: {
|
||||
frappe() {
|
||||
return frappe;
|
||||
return fyo;
|
||||
},
|
||||
platform() {
|
||||
switch (process.platform) {
|
||||
@ -66,8 +66,8 @@ import { setLanguageMap, stringifyCircular } from './utils';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
t: frappe.t,
|
||||
T: frappe.T,
|
||||
t: fyo.t,
|
||||
T: fyo.T,
|
||||
},
|
||||
});
|
||||
|
||||
@ -88,7 +88,7 @@ import { setLanguageMap, stringifyCircular } from './utils';
|
||||
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();
|
||||
app.mount('body');
|
||||
|
||||
@ -100,4 +100,3 @@ import { setLanguageMap, stringifyCircular } from './utils';
|
||||
handleError(true, error, {}, () => process.exit(1));
|
||||
});
|
||||
})();
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import PageHeader from '@/components/PageHeader';
|
||||
import SearchBar from '@/components/SearchBar';
|
||||
import { openQuickEdit } from '@/utils';
|
||||
import PageHeader from 'src/components/PageHeader';
|
||||
import SearchBar from 'src/components/SearchBar';
|
||||
import { openQuickEdit } from 'src/utils';
|
||||
import frappe from 'frappe';
|
||||
import { nextTick } from 'vue';
|
||||
import { handleErrorWithDialog } from '../errorHandling';
|
||||
@ -135,10 +135,10 @@ import { handleErrorWithDialog } from '../errorHandling';
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import PageHeader from '@/components/PageHeader';
|
||||
import SearchBar from '@/components/SearchBar';
|
||||
import { openQuickEdit } from '@/utils';
|
||||
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 { handleErrorWithDialog } from '../errorHandling';
|
||||
|
||||
|
@ -101,12 +101,12 @@
|
||||
</template>
|
||||
<script>
|
||||
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 { getDatesAndPeriodicity } from './getDatesAndPeriodicity';
|
||||
import LineChart from '@/components/Charts/LineChart.vue';
|
||||
import { getYMax } from '@/components/Charts/chartUtils';
|
||||
import { formatXLabels } from '@/utils';
|
||||
import PeriodSelector from './PeriodSelector';
|
||||
|
||||
export default {
|
||||
name: 'Cashflow',
|
||||
|
@ -23,12 +23,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PageHeader from '@/components/PageHeader';
|
||||
import SearchBar from '@/components/SearchBar';
|
||||
import PageHeader from 'src/components/PageHeader';
|
||||
import SearchBar from 'src/components/SearchBar';
|
||||
import Cashflow from './Cashflow';
|
||||
import UnpaidInvoices from './UnpaidInvoices';
|
||||
import ProfitAndLoss from './ProfitAndLoss';
|
||||
import Expenses from './Expenses';
|
||||
import ProfitAndLoss from './ProfitAndLoss';
|
||||
import UnpaidInvoices from './UnpaidInvoices';
|
||||
|
||||
export default {
|
||||
name: 'Dashboard',
|
||||
|
@ -50,8 +50,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import theme from '@/theme';
|
||||
import frappe from 'frappe';
|
||||
import theme from 'src/theme';
|
||||
import DonutChart from '../../components/Charts/DonutChart.vue';
|
||||
import { getDatesAndPeriodicity } from './getDatesAndPeriodicity';
|
||||
import PeriodSelector from './PeriodSelector';
|
||||
|
@ -36,8 +36,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dropdown from '@/components/Dropdown';
|
||||
import { t } from 'frappe';
|
||||
import Dropdown from 'src/components/Dropdown';
|
||||
|
||||
export default {
|
||||
name: 'PeriodSelector',
|
||||
|
@ -29,13 +29,13 @@
|
||||
</template>
|
||||
<script>
|
||||
import frappe from 'frappe';
|
||||
import PeriodSelector from './PeriodSelector';
|
||||
import SectionHeader from './SectionHeader';
|
||||
import BarChart from 'src/components/Charts/BarChart.vue';
|
||||
import { getYMax, getYMin } from 'src/components/Charts/chartUtils';
|
||||
import { formatXLabels } from 'src/utils';
|
||||
import ProfitAndLoss from '../../../reports/ProfitAndLoss/ProfitAndLoss';
|
||||
import { getDatesAndPeriodicity } from './getDatesAndPeriodicity';
|
||||
import BarChart from '@/components/Charts/BarChart.vue';
|
||||
import { getYMax, getYMin } from '@/components/Charts/chartUtils';
|
||||
import { formatXLabels } from '@/utils';
|
||||
import PeriodSelector from './PeriodSelector';
|
||||
import SectionHeader from './SectionHeader';
|
||||
|
||||
export default {
|
||||
name: 'ProfitAndLoss',
|
||||
|
@ -68,9 +68,9 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Button from '@/components/Button';
|
||||
import { routeTo } from '@/utils';
|
||||
import frappe, { t } from 'frappe';
|
||||
import Button from 'src/components/Button';
|
||||
import { routeTo } from 'src/utils';
|
||||
import { getDatesAndPeriodicity } from './getDatesAndPeriodicity';
|
||||
import PeriodSelector from './PeriodSelector';
|
||||
import SectionHeader from './SectionHeader';
|
||||
|
@ -336,17 +336,17 @@
|
||||
</div>
|
||||
</template>
|
||||
<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 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';
|
||||
export default {
|
||||
components: {
|
||||
@ -550,7 +550,7 @@ export default {
|
||||
if (this.isRequiredUnassigned) {
|
||||
showMessageDialog({
|
||||
message: this.t`Required Fields not Assigned`,
|
||||
description: this
|
||||
detail: this
|
||||
.t`Please assign the following fields ${this.requiredUnassigned.join(
|
||||
', '
|
||||
)}`,
|
||||
@ -561,7 +561,7 @@ export default {
|
||||
if (this.importer.assignedMatrix.length === 0) {
|
||||
showMessageDialog({
|
||||
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;
|
||||
}
|
||||
@ -572,7 +572,7 @@ export default {
|
||||
if (!success) {
|
||||
showMessageDialog({
|
||||
message: this.t`Import Failed`,
|
||||
description: message,
|
||||
detail: message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -617,7 +617,7 @@ export default {
|
||||
if (!isValid) {
|
||||
showMessageDialog({
|
||||
message: this.t`Bad import data.`,
|
||||
description: this.t`Could not select file.`,
|
||||
detail: this.t`Could not select file.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -158,14 +158,14 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import LanguageSelector from '@/components/Controls/LanguageSelector.vue';
|
||||
import config from '@/config';
|
||||
import { connectToLocalDatabase, createNewDatabase } from '@/initialization';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import fs from 'fs';
|
||||
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 { showErrorDialog } from '../errorHandling';
|
||||
|
||||
export default {
|
||||
name: 'DatabaseSelector',
|
||||
|
@ -84,12 +84,12 @@
|
||||
</template>
|
||||
|
||||
<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 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 { h } from 'vue';
|
||||
|
||||
|
@ -198,20 +198,20 @@
|
||||
</div>
|
||||
</template>
|
||||
<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 frappe from 'frappe';
|
||||
import { getInvoiceStatus } from 'models/helpers';
|
||||
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,
|
||||
getInvoiceStatus,
|
||||
openSettings,
|
||||
routeTo,
|
||||
showMessageDialog
|
||||
} from '@/utils';
|
||||
import frappe from 'frappe';
|
||||
} from 'src/utils';
|
||||
import { handleErrorWithDialog } from '../errorHandling';
|
||||
|
||||
export default {
|
||||
|
@ -128,14 +128,14 @@
|
||||
</div>
|
||||
</template>
|
||||
<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 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';
|
||||
|
||||
export default {
|
||||
|
@ -56,11 +56,11 @@
|
||||
</div>
|
||||
</template>
|
||||
<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 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';
|
||||
|
||||
export default {
|
||||
|
@ -30,12 +30,12 @@
|
||||
</div>
|
||||
</template>
|
||||
<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 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';
|
||||
|
||||
export default {
|
||||
|
@ -51,16 +51,16 @@
|
||||
</div>
|
||||
</template>
|
||||
<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 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';
|
||||
|
||||
export default {
|
||||
|
@ -77,13 +77,13 @@
|
||||
</template>
|
||||
|
||||
<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 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 {
|
||||
name: 'QuickEditForm',
|
||||
|
@ -136,15 +136,15 @@
|
||||
</div>
|
||||
</template>
|
||||
<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 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 { getReportData } from '../../reports/index';
|
||||
import DropdownWithActions from '../components/DropdownWithActions.vue';
|
||||
|
@ -51,14 +51,14 @@
|
||||
</div>
|
||||
</template>
|
||||
<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 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 { h, markRaw } from 'vue';
|
||||
import { callInitializeMoneyMaker, showToast } from '../../utils';
|
||||
|
@ -12,8 +12,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TwoColumnForm from '@/components/TwoColumnForm';
|
||||
import frappe from 'fyo';
|
||||
import TwoColumnForm from 'src/components/TwoColumnForm';
|
||||
|
||||
export default {
|
||||
name: 'TabGeneral',
|
||||
|
@ -46,10 +46,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import FormControl from '@/components/Controls/FormControl';
|
||||
import TwoColumnForm from '@/components/TwoColumnForm';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import frappe from 'fyo';
|
||||
import FormControl from 'src/components/Controls/FormControl';
|
||||
import TwoColumnForm from 'src/components/TwoColumnForm';
|
||||
import { IPC_ACTIONS } from 'utils/messages';
|
||||
|
||||
export default {
|
||||
|
@ -38,17 +38,17 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FormControl from '@/components/Controls/FormControl';
|
||||
import LanguageSelector from '@/components/Controls/LanguageSelector.vue';
|
||||
import TwoColumnForm from '@/components/TwoColumnForm';
|
||||
import frappe from 'fyo';
|
||||
import FormControl from 'src/components/Controls/FormControl';
|
||||
import LanguageSelector from 'src/components/Controls/LanguageSelector.vue';
|
||||
import TwoColumnForm from 'src/components/TwoColumnForm';
|
||||
import config, {
|
||||
ConfigKeys,
|
||||
TelemetrySetting
|
||||
} from '@/config';
|
||||
import { getTelemetryOptions } from '@/telemetry/helpers';
|
||||
import telemetry from '@/telemetry/telemetry';
|
||||
import { checkForUpdates } from '@/utils';
|
||||
import frappe from 'fyo';
|
||||
} from 'src/config';
|
||||
import { getTelemetryOptions } from 'src/telemetry/helpers';
|
||||
import telemetry from 'src/telemetry/telemetry';
|
||||
import { checkForUpdates } from 'src/utils';
|
||||
|
||||
export default {
|
||||
name: 'TabSystem',
|
||||
|
@ -89,17 +89,17 @@
|
||||
</template>
|
||||
|
||||
<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 fs from 'fs';
|
||||
import frappe from 'fyo';
|
||||
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 {
|
||||
getErrorMessage,
|
||||
|
@ -32,7 +32,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Button from '@/components/Button.vue';
|
||||
import Button from 'src/components/Button.vue';
|
||||
|
||||
export default {
|
||||
emits: ['primary-clicked', 'secondary-clicked'],
|
||||
|
@ -1,7 +1,7 @@
|
||||
import config from '@/config';
|
||||
import countryList from 'fixtures/countryInfo.json';
|
||||
import frappe from 'fyo';
|
||||
import { DEFAULT_LOCALE } from 'fyo/utils/consts';
|
||||
import countryList from '~/fixtures/countryInfo.json';
|
||||
import config from 'src/config';
|
||||
import importCharts from '../../../accounting/importCOA';
|
||||
import generateTaxes from '../../../models/doctype/Tax/RegionalEntries';
|
||||
import regionalModelUpdates from '../../../models/regionalModelUpdates';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { createNumberSeries } from 'frappe/model/naming';
|
||||
import { DEFAULT_SERIES_START } from 'frappe/utils/consts';
|
||||
import { createNumberSeries } from 'fyo/model/naming';
|
||||
import { DEFAULT_SERIES_START } from 'fyo/utils/consts';
|
||||
import { getValueMapFromList } from 'utils';
|
||||
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';
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { fyo } from '@/initFyo';
|
||||
import { ConfigKeys } from 'fyo/core/types';
|
||||
import { fyo } from 'src/initFyo';
|
||||
|
||||
export function incrementOpenCount() {
|
||||
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 { handleError } from 'src/errorHandling';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { showToast } from 'src/utils/ui';
|
||||
import { IPC_CHANNELS, IPC_MESSAGES } from 'utils/messages';
|
||||
|
||||
export default function registerIpcRendererListeners() {
|
||||
|
@ -1,17 +1,17 @@
|
||||
import ChartOfAccounts from '@/pages/ChartOfAccounts.vue';
|
||||
import Dashboard from '@/pages/Dashboard/Dashboard.vue';
|
||||
import DataImport from '@/pages/DataImport.vue';
|
||||
import GetStarted from '@/pages/GetStarted.vue';
|
||||
import InvoiceForm from '@/pages/InvoiceForm.vue';
|
||||
import JournalEntryForm from '@/pages/JournalEntryForm.vue';
|
||||
import ListView from '@/pages/ListView/ListView.vue';
|
||||
import PrintView from '@/pages/PrintView/PrintView.vue';
|
||||
import QuickEditForm from '@/pages/QuickEditForm.vue';
|
||||
import Report from '@/pages/Report.vue';
|
||||
import Settings from '@/pages/Settings/Settings.vue';
|
||||
import { NounEnum, Verb } from 'fyo/telemetry/types';
|
||||
import ChartOfAccounts from 'src/pages/ChartOfAccounts.vue';
|
||||
import Dashboard from 'src/pages/Dashboard/Dashboard.vue';
|
||||
import DataImport from 'src/pages/DataImport.vue';
|
||||
import GetStarted from 'src/pages/GetStarted.vue';
|
||||
import InvoiceForm from 'src/pages/InvoiceForm.vue';
|
||||
import JournalEntryForm from 'src/pages/JournalEntryForm.vue';
|
||||
import ListView from 'src/pages/ListView/ListView.vue';
|
||||
import PrintView from 'src/pages/PrintView/PrintView.vue';
|
||||
import QuickEditForm from 'src/pages/QuickEditForm.vue';
|
||||
import Report from 'src/pages/Report.vue';
|
||||
import Settings from 'src/pages/Settings/Settings.vue';
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
import telemetry from '../frappe/telemetry/telemetry';
|
||||
import { NounEnum, Verb } from '../frappe/telemetry/types';
|
||||
import { fyo } from './initFyo';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
@ -112,7 +112,7 @@ const routes = [
|
||||
},
|
||||
];
|
||||
|
||||
let router = createRouter({ routes, history: createWebHistory() });
|
||||
const router = createRouter({ routes, history: createWebHistory() });
|
||||
|
||||
function removeDetails(path) {
|
||||
if (!path) {
|
||||
@ -133,7 +133,7 @@ router.afterEach((to, from) => {
|
||||
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') {
|
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',
|
||||
};
|
||||
|
||||
const getValidColor = (color) => {
|
||||
const getValidColor = (color: string) => {
|
||||
const isValid = ['gray', 'orange', 'green', 'red', 'yellow', 'blue'].includes(
|
||||
color
|
||||
);
|
||||
return isValid ? color : 'gray';
|
||||
};
|
||||
|
||||
export function getBgColorClass(color) {
|
||||
export function getBgColorClass(color: string) {
|
||||
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}`;
|
||||
}
|
||||
|
||||
export function getTextColorClass(color) {
|
||||
export function getTextColorClass(color: string) {
|
||||
return `text-${getValidColor(color)}-600`;
|
||||
}
|
||||
|
||||
export function getBgTextColorClass(color) {
|
||||
export function getBgTextColorClass(color: string) {
|
||||
const bg = getBgColorClass(color);
|
||||
const text = getTextColorClass(color);
|
||||
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": ".",
|
||||
"types": ["webpack-env"],
|
||||
"paths": {
|
||||
"@/*": ["src/*"],
|
||||
"src/*": ["src/*"],
|
||||
"schemas/*": ["schemas/*"],
|
||||
"backend/*": ["backend/*"],
|
||||
"regional/*": ["regional/*"],
|
||||
"fixtures/*": ["fixtures/*"],
|
||||
"utils/*": ["utils/*"]
|
||||
},
|
||||
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
|
||||
@ -43,7 +44,8 @@
|
||||
"accounting/**/*.ts",
|
||||
|
||||
"scripts/**/*.ts",
|
||||
"utils/csvParser.ts"
|
||||
, "utils/config.ts" ],
|
||||
"utils/csvParser.ts",
|
||||
"utils/config.ts"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
@ -68,3 +68,14 @@ export function titleCase(phrase: string): string {
|
||||
})
|
||||
.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) {
|
||||
Object.assign(config.resolve.alias, {
|
||||
fyo: path.resolve(__dirname, './fyo'),
|
||||
'~': path.resolve('.'),
|
||||
src: path.resolve(__dirname, './src'),
|
||||
schemas: path.resolve(__dirname, './schemas'),
|
||||
backend: path.resolve(__dirname, './backend'),
|
||||
utils: path.resolve(__dirname, './utils'),
|
||||
regional: path.resolve(__dirname, './regional'),
|
||||
fixtures: path.resolve(__dirname, './fixtures'),
|
||||
});
|
||||
|
||||
config.plugins.push(
|
||||
|
Loading…
Reference in New Issue
Block a user