mirror of
https://github.com/frappe/books.git
synced 2024-12-22 19:09:01 +00:00
fix(ux): display can't delete cause link
- update reports on ledger entries - pull data until .plus({days: 1}) to account for all entries of the day
This commit is contained in:
parent
69f8ef11fe
commit
3975eb3f64
@ -1,8 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
CannotCommitError,
|
CannotCommitError,
|
||||||
DatabaseError,
|
getDbError,
|
||||||
DuplicateEntryError,
|
|
||||||
LinkValidationError,
|
|
||||||
NotFoundError,
|
NotFoundError,
|
||||||
ValueError,
|
ValueError,
|
||||||
} from 'fyo/utils/errors';
|
} from 'fyo/utils/errors';
|
||||||
@ -98,7 +96,7 @@ export default class DatabaseCore extends DatabaseBase {
|
|||||||
async connect() {
|
async connect() {
|
||||||
this.knex = knex(this.connectionParams);
|
this.knex = knex(this.connectionParams);
|
||||||
this.knex.on('query-error', (error) => {
|
this.knex.on('query-error', (error) => {
|
||||||
error.type = this.#getError(error);
|
error.type = getDbError(error);
|
||||||
});
|
});
|
||||||
await this.knex.raw('PRAGMA foreign_keys=ON');
|
await this.knex.raw('PRAGMA foreign_keys=ON');
|
||||||
}
|
}
|
||||||
@ -116,7 +114,7 @@ export default class DatabaseCore extends DatabaseBase {
|
|||||||
try {
|
try {
|
||||||
// await this.knex!.raw('commit');
|
// await this.knex!.raw('commit');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const type = this.#getError(err as Error);
|
const type = getDbError(err as Error);
|
||||||
if (type !== CannotCommitError) {
|
if (type !== CannotCommitError) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
@ -155,7 +153,7 @@ export default class DatabaseCore extends DatabaseBase {
|
|||||||
}
|
}
|
||||||
row = await qb.limit(1);
|
row = await qb.limit(1);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (this.#getError(err as Error) !== NotFoundError) {
|
if (getDbError(err as Error) !== NotFoundError) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,7 +291,7 @@ export default class DatabaseCore extends DatabaseBase {
|
|||||||
try {
|
try {
|
||||||
values = await builder.select('fieldname', 'value', 'parent');
|
values = await builder.select('fieldname', 'value', 'parent');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (this.#getError(err as Error) === NotFoundError) {
|
if (getDbError(err as Error) === NotFoundError) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,23 +362,6 @@ export default class DatabaseCore extends DatabaseBase {
|
|||||||
this.prestigeTheTable(schemaName, tableRows);
|
this.prestigeTheTable(schemaName, tableRows);
|
||||||
}
|
}
|
||||||
|
|
||||||
#getError(err: Error) {
|
|
||||||
let errorType = DatabaseError;
|
|
||||||
if (err.message.includes('SQLITE_ERROR: no such table')) {
|
|
||||||
errorType = NotFoundError;
|
|
||||||
}
|
|
||||||
if (err.message.includes('FOREIGN KEY')) {
|
|
||||||
errorType = LinkValidationError;
|
|
||||||
}
|
|
||||||
if (err.message.includes('SQLITE_ERROR: cannot commit')) {
|
|
||||||
errorType = CannotCommitError;
|
|
||||||
}
|
|
||||||
if (err.message.includes('SQLITE_CONSTRAINT: UNIQUE constraint failed:')) {
|
|
||||||
errorType = DuplicateEntryError;
|
|
||||||
}
|
|
||||||
return errorType;
|
|
||||||
}
|
|
||||||
|
|
||||||
async prestigeTheTable(schemaName: string, tableRows: FieldValueMap[]) {
|
async prestigeTheTable(schemaName: string, tableRows: FieldValueMap[]) {
|
||||||
const max = 200;
|
const max = 200;
|
||||||
|
|
||||||
|
@ -36,7 +36,6 @@ export function validateOptions(field: OptionField, value: string, doc: Doc) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const labels = options.map((o) => o.label).join(', ');
|
|
||||||
throw new ValueError(t`Invalid value ${value} for ${field.label}`);
|
throw new ValueError(t`Invalid value ${value} for ${field.label}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,3 +76,27 @@ export class NotImplemented extends BaseError {
|
|||||||
export class ValueError extends ValidationError {}
|
export class ValueError extends ValidationError {}
|
||||||
export class ConflictError extends ValidationError {}
|
export class ConflictError extends ValidationError {}
|
||||||
export class InvalidFieldError extends ValidationError {}
|
export class InvalidFieldError extends ValidationError {}
|
||||||
|
|
||||||
|
export function getDbError(err: Error) {
|
||||||
|
if (!err.message) {
|
||||||
|
return DatabaseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err.message.includes('SQLITE_ERROR: no such table')) {
|
||||||
|
return NotFoundError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err.message.includes('FOREIGN KEY')) {
|
||||||
|
return LinkValidationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err.message.includes('SQLITE_ERROR: cannot commit')) {
|
||||||
|
return CannotCommitError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err.message.includes('SQLITE_CONSTRAINT: UNIQUE constraint failed:')) {
|
||||||
|
return DuplicateEntryError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DatabaseError;
|
||||||
|
}
|
||||||
|
@ -50,7 +50,7 @@ export abstract class AccountReport extends LedgerReport {
|
|||||||
|
|
||||||
async setDefaultFilters(): Promise<void> {
|
async setDefaultFilters(): Promise<void> {
|
||||||
if (this.basedOn === 'Until Date' && !this.toDate) {
|
if (this.basedOn === 'Until Date' && !this.toDate) {
|
||||||
this.toDate = DateTime.now().toISODate();
|
this.toDate = DateTime.now().plus({ days: 1 }).toISODate();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.basedOn === 'Fiscal Year' && !this.toYear) {
|
if (this.basedOn === 'Fiscal Year' && !this.toYear) {
|
||||||
|
@ -23,9 +23,9 @@ export class BalanceSheet extends AccountReport {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
async setReportData(filter?: string) {
|
async setReportData(filter?: string, force?: boolean) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
if (filter !== 'hideGroupAmounts') {
|
if (force || filter !== 'hideGroupAmounts') {
|
||||||
await this._setRawData();
|
await this._setRawData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,11 +86,14 @@ export class BalanceSheet extends AccountReport {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalNode = await this.getTotalNode(row.rootNode, totalName);
|
|
||||||
const totalRow = this.getRowFromAccountListNode(totalNode);
|
|
||||||
|
|
||||||
reportData.push(...row.rows);
|
reportData.push(...row.rows);
|
||||||
reportData.push(totalRow);
|
|
||||||
|
if (row.rootNode) {
|
||||||
|
const totalNode = await this.getTotalNode(row.rootNode, totalName);
|
||||||
|
const totalRow = this.getRowFromAccountListNode(totalNode);
|
||||||
|
reportData.push(totalRow);
|
||||||
|
}
|
||||||
|
|
||||||
reportData.push(emptyRow);
|
reportData.push(emptyRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,15 +37,15 @@ export class GeneralLedger extends LedgerReport {
|
|||||||
|
|
||||||
async setDefaultFilters() {
|
async setDefaultFilters() {
|
||||||
if (!this.toDate) {
|
if (!this.toDate) {
|
||||||
this.toDate = DateTime.now().toISODate();
|
this.toDate = DateTime.now().plus({ days: 1 }).toISODate();
|
||||||
this.fromDate = DateTime.now().minus({ years: 1 }).toISODate();
|
this.fromDate = DateTime.now().minus({ years: 1 }).toISODate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async setReportData(filter?: string) {
|
async setReportData(filter?: string, force?: boolean) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
let sort = true;
|
let sort = true;
|
||||||
if (filter !== 'grouped' || this._rawData.length === 0) {
|
if (force || filter !== 'grouped' || this._rawData.length === 0) {
|
||||||
await this._setRawData();
|
await this._setRawData();
|
||||||
sort = false;
|
sort = false;
|
||||||
}
|
}
|
||||||
@ -141,6 +141,10 @@ export class GeneralLedger extends LedgerReport {
|
|||||||
value = String(value);
|
value = String(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fieldname === 'referenceType') {
|
||||||
|
value = this.fyo.schemaMap[value]?.label ?? value;
|
||||||
|
}
|
||||||
|
|
||||||
row.cells.push({
|
row.cells.push({
|
||||||
italics: entry.name === -1,
|
italics: entry.name === -1,
|
||||||
bold: entry.name === -2,
|
bold: entry.name === -2,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { t } from 'fyo';
|
import { Fyo, t } from 'fyo';
|
||||||
import { Action } from 'fyo/model/types';
|
import { Action } from 'fyo/model/types';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import { Report } from 'reports/Report';
|
import { Report } from 'reports/Report';
|
||||||
@ -13,6 +13,26 @@ export abstract class LedgerReport extends Report {
|
|||||||
static reportName = 'general-ledger';
|
static reportName = 'general-ledger';
|
||||||
|
|
||||||
_rawData: LedgerEntry[] = [];
|
_rawData: LedgerEntry[] = [];
|
||||||
|
shouldRefresh: boolean = false;
|
||||||
|
|
||||||
|
constructor(fyo: Fyo) {
|
||||||
|
super(fyo);
|
||||||
|
this._setObservers();
|
||||||
|
}
|
||||||
|
|
||||||
|
_setObservers() {
|
||||||
|
const listener = () => (this.shouldRefresh = true);
|
||||||
|
|
||||||
|
this.fyo.doc.observer.on(
|
||||||
|
`sync:${ModelNameEnum.AccountingLedgerEntry}`,
|
||||||
|
listener
|
||||||
|
);
|
||||||
|
|
||||||
|
this.fyo.doc.observer.on(
|
||||||
|
`delete:${ModelNameEnum.AccountingLedgerEntry}`,
|
||||||
|
listener
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
_getGroupByKey() {
|
_getGroupByKey() {
|
||||||
let groupBy: GroupByKey = 'referenceName';
|
let groupBy: GroupByKey = 'referenceName';
|
||||||
|
@ -23,9 +23,9 @@ export class ProfitAndLoss extends AccountReport {
|
|||||||
return [AccountRootTypeEnum.Income, AccountRootTypeEnum.Expense];
|
return [AccountRootTypeEnum.Income, AccountRootTypeEnum.Expense];
|
||||||
}
|
}
|
||||||
|
|
||||||
async setReportData(filter?: string) {
|
async setReportData(filter?: string, force?: boolean) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
if (filter !== 'hideGroupAmounts') {
|
if (force || filter !== 'hideGroupAmounts') {
|
||||||
await this._setRawData();
|
await this._setRawData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,6 +77,10 @@ export class ProfitAndLoss extends AccountReport {
|
|||||||
incomeRoot: AccountTreeNode,
|
incomeRoot: AccountTreeNode,
|
||||||
expenseRoot: AccountTreeNode
|
expenseRoot: AccountTreeNode
|
||||||
): Promise<ReportData> {
|
): Promise<ReportData> {
|
||||||
|
if (!incomeRoot || !expenseRoot) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const totalIncome = await this.getTotalNode(
|
const totalIncome = await this.getTotalNode(
|
||||||
incomeRoot,
|
incomeRoot,
|
||||||
t`Total Income (Credit)`
|
t`Total Income (Credit)`
|
||||||
|
@ -14,6 +14,7 @@ export abstract class Report extends Observable<RawValue> {
|
|||||||
filters: Field[] = [];
|
filters: Field[] = [];
|
||||||
reportData: ReportData;
|
reportData: ReportData;
|
||||||
usePagination: boolean = false;
|
usePagination: boolean = false;
|
||||||
|
shouldRefresh: boolean = false;
|
||||||
abstract loading: boolean;
|
abstract loading: boolean;
|
||||||
|
|
||||||
constructor(fyo: Fyo) {
|
constructor(fyo: Fyo) {
|
||||||
@ -75,15 +76,15 @@ export abstract class Report extends Observable<RawValue> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (callPostSet) {
|
if (callPostSet) {
|
||||||
await this.postSet(key);
|
await this.updateData(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async postSet(key?: string) {
|
async updateData(key?: string, force?: boolean) {
|
||||||
await this.setDefaultFilters();
|
await this.setDefaultFilters();
|
||||||
this.filters = await this.getFilters();
|
this.filters = await this.getFilters();
|
||||||
this.columns = await this.getColumns();
|
this.columns = await this.getColumns();
|
||||||
await this.setReportData(key);
|
await this.setReportData(key, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -94,5 +95,5 @@ export abstract class Report extends Observable<RawValue> {
|
|||||||
abstract getActions(): Action[];
|
abstract getActions(): Action[];
|
||||||
abstract getFilters(): Field[] | Promise<Field[]>;
|
abstract getFilters(): Field[] | Promise<Field[]>;
|
||||||
abstract getColumns(): ColumnField[] | Promise<ColumnField[]>;
|
abstract getColumns(): ColumnField[] | Promise<ColumnField[]>;
|
||||||
abstract setReportData(filter?: string): Promise<void>;
|
abstract setReportData(filter?: string, force?: boolean): Promise<void>;
|
||||||
}
|
}
|
||||||
|
@ -53,9 +53,9 @@ export class TrialBalance extends AccountReport {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
async setReportData(filter?: string) {
|
async setReportData(filter?: string, force?: boolean) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
if (filter !== 'hideGroupAmounts') {
|
if (force || filter !== 'hideGroupAmounts') {
|
||||||
await this._setRawData();
|
await this._setRawData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import { truncate } from 'lodash';
|
|||||||
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
|
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
|
||||||
import { fyo } from './initFyo';
|
import { fyo } from './initFyo';
|
||||||
import router from './router';
|
import router from './router';
|
||||||
import { getErrorMessage } from './utils';
|
import { getErrorMessage, stringifyCircular } from './utils';
|
||||||
import { MessageDialogOptions, ToastOptions } from './utils/types';
|
import { MessageDialogOptions, ToastOptions } from './utils/types';
|
||||||
import { showMessageDialog, showToast } from './utils/ui';
|
import { showMessageDialog, showToast } from './utils/ui';
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ async function reportError(errorLogObj: ErrorLog) {
|
|||||||
error_name: errorLogObj.name,
|
error_name: errorLogObj.name,
|
||||||
message: errorLogObj.message,
|
message: errorLogObj.message,
|
||||||
stack: errorLogObj.stack,
|
stack: errorLogObj.stack,
|
||||||
more: JSON.stringify(errorLogObj.more ?? {}),
|
more: stringifyCircular(errorLogObj.more ?? {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (fyo.store.isDevelopment) {
|
if (fyo.store.isDevelopment) {
|
||||||
|
@ -77,7 +77,7 @@ export default {
|
|||||||
let colors = ['#2490EF', '#B7BFC6'];
|
let colors = ['#2490EF', '#B7BFC6'];
|
||||||
if (!this.hasData) {
|
if (!this.hasData) {
|
||||||
data = dummyData;
|
data = dummyData;
|
||||||
colors = ['#E9EBED', '#B7BFC6'];
|
colors = ['#E9EBED', '#DFE1E2'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const xLabels = data.map((cf) => cf['yearmonth']);
|
const xLabels = data.map((cf) => cf['yearmonth']);
|
||||||
|
@ -74,7 +74,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (filterKeys.length) {
|
if (filterKeys.length) {
|
||||||
await this.report.postSet()
|
await this.report.postSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fyo.store.isDevelopment) {
|
if (fyo.store.isDevelopment) {
|
||||||
@ -117,6 +117,8 @@ export default defineComponent({
|
|||||||
|
|
||||||
if (!this.report.reportData.length) {
|
if (!this.report.reportData.length) {
|
||||||
await this.report.setReportData();
|
await this.report.setReportData();
|
||||||
|
} else if (this.report.shouldRefresh) {
|
||||||
|
await this.report.setReportData(undefined, true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -9,7 +9,7 @@ import { fyo } from 'src/initFyo';
|
|||||||
export function getDatesAndPeriodList(
|
export function getDatesAndPeriodList(
|
||||||
period: 'This Year' | 'This Quarter' | 'This Month'
|
period: 'This Year' | 'This Quarter' | 'This Month'
|
||||||
): { periodList: DateTime[]; fromDate: DateTime; toDate: DateTime } {
|
): { periodList: DateTime[]; fromDate: DateTime; toDate: DateTime } {
|
||||||
const toDate: DateTime = DateTime.now();
|
const toDate: DateTime = DateTime.now().plus({ days: 1 });
|
||||||
let fromDate: DateTime;
|
let fromDate: DateTime;
|
||||||
|
|
||||||
if (period === 'This Year') {
|
if (period === 'This Year') {
|
||||||
|
@ -417,7 +417,7 @@ export class Search {
|
|||||||
_setFilterDefaults() {
|
_setFilterDefaults() {
|
||||||
const totalChildKeywords = Object.values(this.searchables)
|
const totalChildKeywords = Object.values(this.searchables)
|
||||||
.filter((s) => s.isChild)
|
.filter((s) => s.isChild)
|
||||||
.map((s) => this.keywords[s.schemaName].length)
|
.map((s) => this.keywords[s.schemaName]?.length ?? 0)
|
||||||
.reduce((a, b) => a + b);
|
.reduce((a, b) => a + b);
|
||||||
|
|
||||||
if (totalChildKeywords > 2_000) {
|
if (totalChildKeywords > 2_000) {
|
||||||
|
@ -7,6 +7,7 @@ import { t } from 'fyo';
|
|||||||
import { Doc } from 'fyo/model/doc';
|
import { Doc } from 'fyo/model/doc';
|
||||||
import { Action } from 'fyo/model/types';
|
import { Action } from 'fyo/model/types';
|
||||||
import { getActions } from 'fyo/utils';
|
import { getActions } from 'fyo/utils';
|
||||||
|
import { getDbError, LinkValidationError } from 'fyo/utils/errors';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import { handleErrorWithDialog } from 'src/errorHandling';
|
import { handleErrorWithDialog } from 'src/errorHandling';
|
||||||
import { fyo } from 'src/initFyo';
|
import { fyo } from 'src/initFyo';
|
||||||
@ -106,7 +107,7 @@ export async function showToast(options: ToastOptions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.st = showToast
|
window.st = showToast;
|
||||||
|
|
||||||
function replaceAndAppendMount(app: App<Element>, replaceId: string) {
|
function replaceAndAppendMount(app: App<Element>, replaceId: string) {
|
||||||
const fragment = document.createDocumentFragment();
|
const fragment = document.createDocumentFragment();
|
||||||
@ -162,7 +163,15 @@ export async function deleteDocWithPrompt(doc: Doc) {
|
|||||||
await doc.delete();
|
await doc.delete();
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
handleErrorWithDialog(err as Error, doc);
|
if (getDbError(err as Error) === LinkValidationError) {
|
||||||
|
showMessageDialog({
|
||||||
|
message: t`Delete Failed`,
|
||||||
|
detail: t`Cannot delete ${schemaLabel} ${doc.name!} because of linked entries.`,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
handleErrorWithDialog(err as Error, doc);
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user