2
0
mirror of https://github.com/frappe/books.git synced 2024-11-10 07:40:55 +00:00

Merge pull request #434 from 18alantom/minor-fixes-three

Minor fixes three
This commit is contained in:
Alan 2022-07-30 10:56:43 -07:00 committed by GitHub
commit 5899f92c9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 143 additions and 78 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 KiB

After

Width:  |  Height:  |  Size: 145 KiB

BIN
.github/logo.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -2,7 +2,7 @@ import {
CannotCommitError,
getDbError,
NotFoundError,
ValueError
ValueError,
} from 'fyo/utils/errors';
import { knex, Knex } from 'knex';
import {
@ -11,12 +11,12 @@ import {
RawValue,
Schema,
SchemaMap,
TargetField
TargetField,
} from '../../schemas/types';
import {
getIsNullOrUndef,
getRandomString,
getValueMapFromList
getValueMapFromList,
} from '../../utils';
import { DatabaseBase, GetAllOptions, QueryFilter } from '../../utils/db/types';
import { getDefaultMetaFieldValueMap, sqliteTypeMap, SYSTEM } from '../helpers';
@ -24,7 +24,7 @@ import {
ColumnDiff,
FieldValueMap,
GetQueryBuilderOptions,
SingleValue
SingleValue,
} from './types';
/**

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 555 B

After

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 802 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 941 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -29,7 +29,7 @@ export async function setupDummyInstance(
baseCount: number = 1000,
notifier?: Notifier
) {
fyo.purgeCache();
await fyo.purgeCache();
notifier?.(fyo.t`Setting Up Instance`, -1);
const options = {
logo: null,

View File

@ -107,7 +107,7 @@ export class AuthHandler {
// TODO: Implement this with auth flow
}
purgeCache() {}
async purgeCache() {}
#getServerURL() {
return this.#config.serverURL || '';

View File

@ -85,7 +85,8 @@ export class DatabaseHandler extends DatabaseBase {
}
}
purgeCache() {
async purgeCache() {
await this.close();
this.dbPath = undefined;
this.#schemaMap = {};
this.fieldValueMap = {};
@ -214,7 +215,6 @@ export class DatabaseHandler extends DatabaseBase {
async close(): Promise<void> {
await this.#demux.call('close');
this.purgeCache();
}
/**

View File

@ -26,7 +26,7 @@ export class DocHandler {
this.observer = new Observable();
}
purgeCache() {
async purgeCache() {
this.init();
}

View File

@ -2,7 +2,7 @@ import { ipcRenderer } from 'electron';
import { DatabaseError, NotImplemented } from 'fyo/utils/errors';
import { SchemaMap } from 'schemas/types';
import { DatabaseDemuxBase, DatabaseMethod } from 'utils/db/types';
import { DatabaseResponse } from 'utils/ipc/types';
import { BackendResponse } from 'utils/ipc/types';
import { IPC_ACTIONS } from 'utils/messages';
export class DatabaseDemux extends DatabaseDemuxBase {
@ -12,7 +12,7 @@ export class DatabaseDemux extends DatabaseDemuxBase {
this.#isElectron = isElectron;
}
async #handleDBCall(func: () => Promise<DatabaseResponse>): Promise<unknown> {
async #handleDBCall(func: () => Promise<BackendResponse>): Promise<unknown> {
const response = await func();
if (response.error?.name) {

View File

@ -202,7 +202,7 @@ export class Fyo {
return value;
}
purgeCache() {
async purgeCache() {
this.pesa = getMoneyMaker({
currency: DEFAULT_CURRENCY,
precision: DEFAULT_INTERNAL_PRECISION,
@ -216,9 +216,9 @@ export class Fyo {
this.currencySymbols = {};
this.errorLog = [];
this.temp = {};
this.db.purgeCache();
this.auth.purgeCache();
this.doc.purgeCache();
await this.db.purgeCache();
await this.auth.purgeCache();
await this.doc.purgeCache();
}
store = {

View File

@ -126,6 +126,30 @@ export class Doc extends Observable<DocValue | Doc[]> {
return this._syncing;
}
get canDelete() {
if (this.notInserted) {
return false;
}
if (this.schema.isSingle) {
return false;
}
if (!this.schema.isSubmittable) {
return true;
}
if (this.schema.isSubmittable && this.isCancelled) {
return true;
}
if (this.schema.isSubmittable && !this.isSubmitted) {
return true;
}
return false;
}
_setValuesWithoutChecks(data: DocValueMap) {
for (const field of this.schema.fields) {
const fieldname = field.fieldname;
@ -694,7 +718,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
}
async delete() {
if (this.schema.isSubmittable && !this.isCancelled) {
if (!this.canDelete) {
return;
}

View File

@ -34,7 +34,7 @@ describe('Fyo Init', function () {
true,
'non zero schemas'
);
await fyo.db.close();
await fyo.db.purgeCache();
});
});

View File

@ -1,74 +1,76 @@
export class BaseError extends Error {
message: string;
statusCode: number;
shouldStore: boolean;
constructor(statusCode: number, message: string) {
constructor(statusCode: number, message: string, shouldStore: boolean = true) {
super(message);
this.name = 'BaseError';
this.statusCode = statusCode;
this.message = message;
this.shouldStore = shouldStore;
}
}
export class ValidationError extends BaseError {
constructor(message: string) {
super(417, message);
constructor(message: string, shouldStore: boolean = false) {
super(417, message, shouldStore);
this.name = 'ValidationError';
}
}
export class NotFoundError extends BaseError {
constructor(message: string) {
super(404, message);
constructor(message: string, shouldStore: boolean = true) {
super(404, message, shouldStore);
this.name = 'NotFoundError';
}
}
export class ForbiddenError extends BaseError {
constructor(message: string) {
super(403, message);
constructor(message: string, shouldStore: boolean = true) {
super(403, message, shouldStore);
this.name = 'ForbiddenError';
}
}
export class DuplicateEntryError extends ValidationError {
constructor(message: string) {
super(message);
constructor(message: string, shouldStore: boolean = false) {
super(message, shouldStore);
this.name = 'DuplicateEntryError';
}
}
export class LinkValidationError extends ValidationError {
constructor(message: string) {
super(message);
constructor(message: string, shouldStore: boolean = false) {
super(message, shouldStore);
this.name = 'LinkValidationError';
}
}
export class MandatoryError extends ValidationError {
constructor(message: string) {
super(message);
constructor(message: string, shouldStore: boolean = false) {
super(message, shouldStore);
this.name = 'MandatoryError';
}
}
export class DatabaseError extends BaseError {
constructor(message: string) {
super(500, message);
constructor(message: string, shouldStore: boolean = true) {
super(500, message, shouldStore);
this.name = 'DatabaseError';
}
}
export class CannotCommitError extends DatabaseError {
constructor(message: string) {
super(message);
constructor(message: string, shouldStore: boolean = true) {
super(message, shouldStore);
this.name = 'CannotCommitError';
}
}
export class NotImplemented extends BaseError {
constructor() {
super(501, '');
constructor(message: string = '', shouldStore: boolean = false) {
super(501, message, shouldStore);
this.name = 'NotImplemented';
}
}

View File

@ -3,7 +3,7 @@ import fs from 'fs/promises';
import { ConfigFile, ConfigKeys } from 'fyo/core/types';
import { Main } from 'main';
import config from 'utils/config';
import { DatabaseResponse } from 'utils/ipc/types';
import { BackendResponse } from 'utils/ipc/types';
import { IPC_CHANNELS } from 'utils/messages';
interface ConfigFilesWithModified extends ConfigFile {
@ -54,15 +54,16 @@ export async function getConfigFilesWithModified(files: ConfigFile[]) {
}
export async function getErrorHandledReponse(func: () => Promise<unknown>) {
const response: DatabaseResponse = {};
const response: BackendResponse = {};
try {
response.data = await func();
} catch (err) {
response.error = {
name: (err as Error).name,
message: (err as Error).message,
stack: (err as Error).stack,
name: (err as NodeJS.ErrnoException).name,
message: (err as NodeJS.ErrnoException).message,
stack: (err as NodeJS.ErrnoException).stack,
code: (err as NodeJS.ErrnoException).code,
};
}

View File

@ -139,7 +139,7 @@ export default function registerIpcMainActionListeners(main: Main) {
});
ipcMain.handle(IPC_ACTIONS.DELETE_FILE, async (_, filePath) => {
await fs.unlink(filePath);
return getErrorHandledReponse(async () => await fs.unlink(filePath));
});
ipcMain.handle(IPC_ACTIONS.GET_DB_LIST, async (_) => {

View File

@ -1,5 +1,5 @@
import { Fyo, t } from 'fyo';
import { ValidationError } from 'fyo/utils/errors';
import { NotFoundError, ValidationError } from 'fyo/utils/errors';
import { Account } from 'models/baseModels/Account/Account';
import { AccountingLedgerEntry } from 'models/baseModels/AccountingLedgerEntry/AccountingLedgerEntry';
import { ModelNameEnum } from 'models/types';
@ -210,9 +210,20 @@ export class LedgerPosting {
}
async _getRoundOffAccount() {
return (await this.fyo.getValue(
const roundOffAccount = (await this.fyo.getValue(
ModelNameEnum.AccountingSettings,
'roundOffAccount'
)) as string;
if (!roundOffAccount) {
const notFoundError = new NotFoundError(
t`Please set Round Off Account in the Settings.`,
false
);
notFoundError.name = t`Round Off Account Not Found`;
throw notFoundError;
}
return roundOffAccount;
}
}

View File

@ -91,7 +91,8 @@
"fieldname": "outstandingAmount",
"label": "Outstanding Amount",
"fieldtype": "Currency",
"readOnly": true
"readOnly": true,
"filter": true
},
{
"fieldname": "setDiscountAmount",

View File

@ -90,7 +90,8 @@
"fieldname": "outstandingAmount",
"label": "Outstanding Amount",
"fieldtype": "Currency",
"readOnly": true
"readOnly": true,
"filter": true
},
{
"fieldname": "setDiscountAmount",

View File

@ -33,6 +33,7 @@ export interface BaseField {
groupBy?: string; // UI Facing used in dropdowns fields
meta?: boolean; // Field is a meta field, i.e. only for the db, not UI
inline?: boolean; // UI Facing config, whether to display doc inline.
filter?: boolean; // UI Facing config, whether to be used to filter the List.
computed?: boolean; // Computed values are not stored in the database.
}

View File

@ -102,6 +102,7 @@ export default {
'companyName'
);
await this.setSearcher();
await updateConfigFiles(fyo);
},
async setSearcher() {
this.searcher = new Search(fyo);
@ -133,7 +134,6 @@ export default {
}
await initializeInstance(filePath, false, countryCode, fyo);
await updateConfigFiles(fyo);
await this.setDesk(filePath);
},
async setDeskRoute() {
@ -149,7 +149,7 @@ export default {
async showDbSelector() {
fyo.config.set('lastSelectedFilePath', null);
fyo.telemetry.stop();
fyo.purgeCache();
await fyo.purgeCache();
this.activeScreen = 'DatabaseSelector';
this.dbPath = '';
this.searcher = null;

View File

@ -214,10 +214,11 @@ export default {
];
return fyo.schemaMap[this.schemaName].fields.filter(
(f) =>
!f.computed &&
!excludedFieldsTypes.includes(f.fieldtype) &&
!f.meta &&
!f.readOnly
f.filter ||
(!f.computed &&
!excludedFieldsTypes.includes(f.fieldtype) &&
!f.meta &&
!f.readOnly)
);
},
fieldOptions() {

View File

@ -12,11 +12,11 @@
/>
<path
d="M7.73114 2.75564V0.75855H0.0548096V2.75564H7.73114Z"
fill="#0089FF"
fill="#2490EF"
/>
<path
d="M2.48876 7.43646H7.35669V5.43935H0.0548096V12.1796H2.48876V7.43646Z"
fill="#0089FF"
fill="#2490EF"
/>
</svg>
</template>

View File

@ -1,11 +1,7 @@
import { ipcRenderer } from 'electron';
import { t } from 'fyo';
import { Doc } from 'fyo/model/doc';
import {
MandatoryError,
NotFoundError,
ValidationError,
} from 'fyo/utils/errors';
import { BaseError } from 'fyo/utils/errors';
import { ErrorLog } from 'fyo/utils/types';
import { truncate } from 'lodash';
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
@ -16,9 +12,8 @@ import { MessageDialogOptions, ToastOptions } from './utils/types';
import { showMessageDialog, showToast } from './utils/ui';
function shouldNotStore(error: Error) {
return [MandatoryError, ValidationError, NotFoundError].some(
(errorClass) => error instanceof errorClass
);
const shouldLog = (error as BaseError).shouldStore ?? true;
return !shouldLog;
}
async function reportError(errorLogObj: ErrorLog) {
@ -58,18 +53,17 @@ export function getErrorLogObject(
const { name, stack, message } = error;
const errorLogObj = { name, stack, message, more };
// @ts-ignore
fyo.errorLog.push(errorLogObj);
return errorLogObj;
}
export async function handleError(
shouldLog: boolean,
logToConsole: boolean,
error: Error,
more?: Record<string, unknown>
) {
if (shouldLog) {
if (logToConsole) {
console.error(error);
}

View File

@ -11,7 +11,7 @@ async function closeDbIfConnected() {
return;
}
await fyo.db.close();
await fyo.db.purgeCache();
}
export async function initializeInstance(
@ -89,7 +89,7 @@ async function setInstanceId(fyo: Fyo) {
)) as string;
}
async function setCurrencySymbols(fyo: Fyo) {
export async function setCurrencySymbols(fyo: Fyo) {
const currencies = (await fyo.db.getAll(ModelNameEnum.Currency, {
fields: ['name', 'symbol'],
})) as { name: string; symbol: string }[];

View File

@ -299,7 +299,7 @@ export default {
);
updateConfigFiles(fyo);
fyo.purgeCache();
await fyo.purgeCache();
await this.setFiles();
this.creatingDemo = false;

View File

@ -4,7 +4,7 @@
<template #header v-if="doc">
<StatusBadge :status="status" />
<Button
v-if="doc?.submitted"
v-if="!doc.isCancelled && !doc.dirty"
:icon="true"
@click="routeTo(`/print/${doc.schemaName}/${doc.name}`)"
>

View File

@ -10,7 +10,7 @@ import {
import { AccountRootTypeEnum } from 'models/baseModels/Account/types';
import { AccountingSettings } from 'models/baseModels/AccountingSettings/AccountingSettings';
import { ModelNameEnum } from 'models/types';
import { initializeInstance } from 'src/initFyo';
import { initializeInstance, setCurrencySymbols } from 'src/initFyo';
import { createRegionalRecords } from 'src/regional';
import { getRandomString } from 'utils';
import { defaultUOMs } from 'utils/defaults';
@ -40,6 +40,10 @@ export default async function setupInstance(
await createDefaultNumberSeries(fyo);
await completeSetup(companyName, fyo);
if (!Object.keys(fyo.currencySymbols).length) {
await setCurrencySymbols(fyo);
}
fyo.store.skipTelemetryLogging = false;
}

View File

@ -3,9 +3,11 @@
*/
import { ipcRenderer } from 'electron';
import { t } from 'fyo';
import { BaseError } from 'fyo/utils/errors';
import { BackendResponse } from 'utils/ipc/types';
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
import { setLanguageMap } from './language';
import { showToast } from './ui';
import { showMessageDialog, showToast } from './ui';
export async function checkForUpdates() {
await ipcRenderer.invoke(IPC_ACTIONS.CHECK_FOR_UPDATES);
@ -17,7 +19,32 @@ export async function openLink(link: string) {
}
export async function deleteDb(filePath: string) {
await ipcRenderer.invoke(IPC_ACTIONS.DELETE_FILE, filePath);
const { error } = (await ipcRenderer.invoke(
IPC_ACTIONS.DELETE_FILE,
filePath
)) as BackendResponse;
if (error?.code === 'EBUSY') {
showMessageDialog({
message: t`Delete Failed`,
detail: t`Please restart and try again`,
});
} else if (error?.code === 'ENOENT') {
showMessageDialog({
message: t`Delete Failed`,
detail: t`File ${filePath} does not exist`,
});
} else if (error?.code === 'EPERM') {
showMessageDialog({
message: t`Cannot Delete`,
detail: t`Close Frappe Books and try manually`,
});
} else if (error) {
const err = new BaseError(500, error.message);
err.name = error.name;
err.stack = error.stack;
throw err;
}
}
export async function saveData(data: string, savePath: string) {

View File

@ -150,7 +150,7 @@ export async function routeTo(route: string | RouteLocationRaw) {
export async function deleteDocWithPrompt(doc: Doc) {
const schemaLabel = fyo.schemaMap[doc.schemaName]!.label;
let detail = t`This action is permanent.`;
if (doc.isTransactional) {
if (doc.isTransactional && doc.isSubmitted) {
detail = t`This action is permanent and will delete associated ledger entries.`;
}
@ -291,9 +291,7 @@ function getDeleteAction(doc: Doc): Action {
component: {
template: '<span class="text-red-700">{{ t`Delete` }}</span>',
},
condition: (doc: Doc) =>
(!doc.notInserted && !doc.schema.isSubmittable && !doc.schema.isSingle) ||
doc.isCancelled,
condition: (doc: Doc) => doc.canDelete,
async action() {
const res = await deleteDocWithPrompt(doc);
if (res) {

View File

@ -1,4 +1,4 @@
export interface DatabaseResponse {
export interface BackendResponse {
data?: unknown;
error?: { message: string; name: string; stack?: string };
error?: { message: string; name: string; stack?: string; code?: string };
}