mirror of
https://github.com/frappe/books.git
synced 2025-02-08 23:18:31 +00:00
incr: improve error display
- show duplicate error instead of sqlite fail dump
This commit is contained in:
parent
4ebf232e18
commit
d90d113981
@ -19,6 +19,7 @@ import { markRaw } from 'vue';
|
|||||||
import { isPesa } from '../utils/index';
|
import { isPesa } from '../utils/index';
|
||||||
import {
|
import {
|
||||||
areDocValuesEqual,
|
areDocValuesEqual,
|
||||||
|
getInsertionError,
|
||||||
getMissingMandatoryMessage,
|
getMissingMandatoryMessage,
|
||||||
getPreDefaultValues,
|
getPreDefaultValues,
|
||||||
setChildDocIdx,
|
setChildDocIdx,
|
||||||
@ -682,7 +683,12 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
|||||||
await this._preSync();
|
await this._preSync();
|
||||||
|
|
||||||
const validDict = this.getValidDict(false, true);
|
const validDict = this.getValidDict(false, true);
|
||||||
const data = await this.fyo.db.insert(this.schemaName, validDict);
|
let data: DocValueMap;
|
||||||
|
try {
|
||||||
|
data = await this.fyo.db.insert(this.schemaName, validDict);
|
||||||
|
} catch (err) {
|
||||||
|
throw getInsertionError(err as Error, validDict);
|
||||||
|
}
|
||||||
await this._syncValues(data);
|
await this._syncValues(data);
|
||||||
|
|
||||||
this.fyo.telemetry.log(Verb.Created, this.schemaName);
|
this.fyo.telemetry.log(Verb.Created, this.schemaName);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Fyo } from 'fyo';
|
import { Fyo } from 'fyo';
|
||||||
import { DocValue } from 'fyo/core/types';
|
import { DocValue, DocValueMap } from 'fyo/core/types';
|
||||||
import { isPesa } from 'fyo/utils';
|
import { isPesa } from 'fyo/utils';
|
||||||
|
import { DuplicateEntryError } from 'fyo/utils/errors';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
import { Money } from 'pesa';
|
import { Money } from 'pesa';
|
||||||
import { Field, FieldType, FieldTypeEnum } from 'schemas/types';
|
import { Field, FieldType, FieldTypeEnum } from 'schemas/types';
|
||||||
@ -114,3 +115,37 @@ export function setChildDocIdx(childDocs: Doc[]) {
|
|||||||
childDocs[idx].idx = +idx;
|
childDocs[idx].idx = +idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getInsertionError(err: Error, validDict: DocValueMap): Error {
|
||||||
|
if (err.message.includes('UNIQUE constraint failed:')) {
|
||||||
|
return getDuplicateEntryError(err, validDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDuplicateEntryError(
|
||||||
|
err: Error,
|
||||||
|
validDict: DocValueMap
|
||||||
|
): Error | DuplicateEntryError {
|
||||||
|
const matches = err.message.match(/UNIQUE constraint failed:\s(\w+)\.(\w+)$/);
|
||||||
|
if (!matches) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
const schemaName = matches[1];
|
||||||
|
const fieldname = matches[2];
|
||||||
|
if (!schemaName || !fieldname) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
const duplicateEntryError = new DuplicateEntryError(err.message, false);
|
||||||
|
duplicateEntryError.stack = err.stack;
|
||||||
|
duplicateEntryError.more = {
|
||||||
|
schemaName,
|
||||||
|
fieldname,
|
||||||
|
value: validDict[fieldname],
|
||||||
|
};
|
||||||
|
|
||||||
|
return duplicateEntryError;
|
||||||
|
}
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
export class BaseError extends Error {
|
export class BaseError extends Error {
|
||||||
|
more: Record<string, unknown> = {};
|
||||||
message: string;
|
message: string;
|
||||||
statusCode: number;
|
statusCode: number;
|
||||||
shouldStore: boolean;
|
shouldStore: boolean;
|
||||||
|
|
||||||
constructor(statusCode: number, message: string, shouldStore: boolean = true) {
|
constructor(
|
||||||
|
statusCode: number,
|
||||||
|
message: string,
|
||||||
|
shouldStore: boolean = true
|
||||||
|
) {
|
||||||
super(message);
|
super(message);
|
||||||
this.name = 'BaseError';
|
this.name = 'BaseError';
|
||||||
this.statusCode = statusCode;
|
this.statusCode = statusCode;
|
||||||
@ -96,7 +101,7 @@ export function getDbError(err: Error) {
|
|||||||
return CannotCommitError;
|
return CannotCommitError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err.message.includes('SQLITE_CONSTRAINT: UNIQUE constraint failed:')) {
|
if (err.message.includes('UNIQUE constraint failed:')) {
|
||||||
return DuplicateEntryError;
|
return DuplicateEntryError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,8 +94,8 @@ export async function handleErrorWithDialog(
|
|||||||
const errorMessage = getErrorMessage(error, doc);
|
const errorMessage = getErrorMessage(error, doc);
|
||||||
await handleError(false, error, { errorMessage, doc });
|
await handleError(false, error, { errorMessage, doc });
|
||||||
|
|
||||||
const name = error.name ?? t`Error`;
|
const label = getErrorLabel(error);
|
||||||
const options: MessageDialogOptions = { message: name, detail: errorMessage };
|
const options: MessageDialogOptions = { message: label, detail: errorMessage };
|
||||||
|
|
||||||
if (reportError) {
|
if (reportError) {
|
||||||
options.detail = truncate(options.detail, { length: 128 });
|
options.detail = truncate(options.detail, { length: 128 });
|
||||||
@ -106,7 +106,7 @@ export async function handleErrorWithDialog(
|
|||||||
reportIssue(getErrorLogObject(error, { errorMessage }));
|
reportIssue(getErrorLogObject(error, { errorMessage }));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ label: t`OK`, action() {} },
|
{ label: t`Cancel`, action() {} },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,3 +196,52 @@ export function reportIssue(errorLogObj?: ErrorLog) {
|
|||||||
const urlQuery = getIssueUrlQuery(errorLogObj);
|
const urlQuery = getIssueUrlQuery(errorLogObj);
|
||||||
ipcRenderer.send(IPC_MESSAGES.OPEN_EXTERNAL, urlQuery);
|
ipcRenderer.send(IPC_MESSAGES.OPEN_EXTERNAL, urlQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getErrorLabel(error: Error) {
|
||||||
|
const name = error.name;
|
||||||
|
if (!name) {
|
||||||
|
return t`Error`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'BaseError') {
|
||||||
|
return t`Error`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'ValidationError') {
|
||||||
|
return t`Validation Error`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'NotFoundError') {
|
||||||
|
return t`Not Found`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'ForbiddenError') {
|
||||||
|
return t`Forbidden Error`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'DuplicateEntryError') {
|
||||||
|
return t`Duplicate Entry`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'LinkValidationError') {
|
||||||
|
return t`Link Validation Error`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'MandatoryError') {
|
||||||
|
return t`Mandatory Error`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'DatabaseError') {
|
||||||
|
return t`Database Error`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'CannotCommitError') {
|
||||||
|
return t`Cannot Commit Error`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'NotImplemented') {
|
||||||
|
return t`Error`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return t`Error`;
|
||||||
|
}
|
||||||
|
@ -4,9 +4,14 @@
|
|||||||
import { t } from 'fyo';
|
import { t } from 'fyo';
|
||||||
import { Doc } from 'fyo/model/doc';
|
import { Doc } from 'fyo/model/doc';
|
||||||
import { isPesa } from 'fyo/utils';
|
import { isPesa } from 'fyo/utils';
|
||||||
import { DuplicateEntryError, LinkValidationError } from 'fyo/utils/errors';
|
import {
|
||||||
|
BaseError,
|
||||||
|
DuplicateEntryError,
|
||||||
|
LinkValidationError
|
||||||
|
} from 'fyo/utils/errors';
|
||||||
import { Money } from 'pesa';
|
import { Money } from 'pesa';
|
||||||
import { Field, FieldType, FieldTypeEnum } from 'schemas/types';
|
import { Field, FieldType, FieldTypeEnum } from 'schemas/types';
|
||||||
|
import { fyo } from 'src/initFyo';
|
||||||
|
|
||||||
export function stringifyCircular(
|
export function stringifyCircular(
|
||||||
obj: unknown,
|
obj: unknown,
|
||||||
@ -24,7 +29,8 @@ export function stringifyCircular(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (cacheValue.includes(value)) {
|
if (cacheValue.includes(value)) {
|
||||||
const circularKey = cacheKey[cacheValue.indexOf(value)] || '{self}';
|
const circularKey: string =
|
||||||
|
cacheKey[cacheValue.indexOf(value)] || '{self}';
|
||||||
return ignoreCircular ? undefined : `[Circular:${circularKey}]`;
|
return ignoreCircular ? undefined : `[Circular:${circularKey}]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,16 +90,23 @@ export function convertPesaValuesToFloat(obj: Record<string, unknown>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getErrorMessage(e: Error, doc?: Doc): string {
|
export function getErrorMessage(e: Error, doc?: Doc): string {
|
||||||
let errorMessage = e.message || t`An error occurred.`;
|
const errorMessage = e.message || t`An error occurred.`;
|
||||||
|
|
||||||
const { schemaName, name }: { schemaName?: string; name?: string } =
|
let { schemaName, name } = doc ?? {};
|
||||||
doc ?? {};
|
if (!doc) {
|
||||||
const canElaborate = !!(schemaName && name);
|
schemaName = (e as BaseError).more?.schemaName as string | undefined;
|
||||||
|
name = (e as BaseError).more?.value as string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (e instanceof LinkValidationError && canElaborate) {
|
if (!schemaName || !name) {
|
||||||
errorMessage = t`${schemaName} ${name} is linked with existing records.`;
|
return errorMessage;
|
||||||
} else if (e instanceof DuplicateEntryError && canElaborate) {
|
}
|
||||||
errorMessage = t`${schemaName} ${name} already exists.`;
|
|
||||||
|
const label = fyo.db.schemaMap[schemaName]?.label ?? schemaName;
|
||||||
|
if (e instanceof LinkValidationError) {
|
||||||
|
return t`${label} ${name} is linked with existing records.`;
|
||||||
|
} else if (e instanceof DuplicateEntryError) {
|
||||||
|
return t`${label} ${name} already exists.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return errorMessage;
|
return errorMessage;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user