2
0
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:
18alantom 2022-05-30 17:04:25 +05:30
parent 69f8ef11fe
commit 3975eb3f64
16 changed files with 99 additions and 52 deletions

View File

@ -1,8 +1,6 @@
import {
CannotCommitError,
DatabaseError,
DuplicateEntryError,
LinkValidationError,
getDbError,
NotFoundError,
ValueError,
} from 'fyo/utils/errors';
@ -98,7 +96,7 @@ export default class DatabaseCore extends DatabaseBase {
async connect() {
this.knex = knex(this.connectionParams);
this.knex.on('query-error', (error) => {
error.type = this.#getError(error);
error.type = getDbError(error);
});
await this.knex.raw('PRAGMA foreign_keys=ON');
}
@ -116,7 +114,7 @@ export default class DatabaseCore extends DatabaseBase {
try {
// await this.knex!.raw('commit');
} catch (err) {
const type = this.#getError(err as Error);
const type = getDbError(err as Error);
if (type !== CannotCommitError) {
throw err;
}
@ -155,7 +153,7 @@ export default class DatabaseCore extends DatabaseBase {
}
row = await qb.limit(1);
} catch (err) {
if (this.#getError(err as Error) !== NotFoundError) {
if (getDbError(err as Error) !== NotFoundError) {
throw err;
}
}
@ -293,7 +291,7 @@ export default class DatabaseCore extends DatabaseBase {
try {
values = await builder.select('fieldname', 'value', 'parent');
} catch (err) {
if (this.#getError(err as Error) === NotFoundError) {
if (getDbError(err as Error) === NotFoundError) {
return [];
}
@ -364,23 +362,6 @@ export default class DatabaseCore extends DatabaseBase {
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[]) {
const max = 200;

View File

@ -36,7 +36,6 @@ export function validateOptions(field: OptionField, value: string, doc: Doc) {
return;
}
const labels = options.map((o) => o.label).join(', ');
throw new ValueError(t`Invalid value ${value} for ${field.label}`);
}

View File

@ -76,3 +76,27 @@ export class NotImplemented extends BaseError {
export class ValueError extends ValidationError {}
export class ConflictError 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;
}

View File

@ -50,7 +50,7 @@ export abstract class AccountReport extends LedgerReport {
async setDefaultFilters(): Promise<void> {
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) {

View File

@ -23,9 +23,9 @@ export class BalanceSheet extends AccountReport {
];
}
async setReportData(filter?: string) {
async setReportData(filter?: string, force?: boolean) {
this.loading = true;
if (filter !== 'hideGroupAmounts') {
if (force || filter !== 'hideGroupAmounts') {
await this._setRawData();
}
@ -86,11 +86,14 @@ export class BalanceSheet extends AccountReport {
continue;
}
reportData.push(...row.rows);
if (row.rootNode) {
const totalNode = await this.getTotalNode(row.rootNode, totalName);
const totalRow = this.getRowFromAccountListNode(totalNode);
reportData.push(...row.rows);
reportData.push(totalRow);
}
reportData.push(emptyRow);
}

View File

@ -37,15 +37,15 @@ export class GeneralLedger extends LedgerReport {
async setDefaultFilters() {
if (!this.toDate) {
this.toDate = DateTime.now().toISODate();
this.toDate = DateTime.now().plus({ days: 1 }).toISODate();
this.fromDate = DateTime.now().minus({ years: 1 }).toISODate();
}
}
async setReportData(filter?: string) {
async setReportData(filter?: string, force?: boolean) {
this.loading = true;
let sort = true;
if (filter !== 'grouped' || this._rawData.length === 0) {
if (force || filter !== 'grouped' || this._rawData.length === 0) {
await this._setRawData();
sort = false;
}
@ -141,6 +141,10 @@ export class GeneralLedger extends LedgerReport {
value = String(value);
}
if (fieldname === 'referenceType') {
value = this.fyo.schemaMap[value]?.label ?? value;
}
row.cells.push({
italics: entry.name === -1,
bold: entry.name === -2,

View File

@ -1,4 +1,4 @@
import { t } from 'fyo';
import { Fyo, t } from 'fyo';
import { Action } from 'fyo/model/types';
import { ModelNameEnum } from 'models/types';
import { Report } from 'reports/Report';
@ -13,6 +13,26 @@ export abstract class LedgerReport extends Report {
static reportName = 'general-ledger';
_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() {
let groupBy: GroupByKey = 'referenceName';

View File

@ -23,9 +23,9 @@ export class ProfitAndLoss extends AccountReport {
return [AccountRootTypeEnum.Income, AccountRootTypeEnum.Expense];
}
async setReportData(filter?: string) {
async setReportData(filter?: string, force?: boolean) {
this.loading = true;
if (filter !== 'hideGroupAmounts') {
if (force || filter !== 'hideGroupAmounts') {
await this._setRawData();
}
@ -77,6 +77,10 @@ export class ProfitAndLoss extends AccountReport {
incomeRoot: AccountTreeNode,
expenseRoot: AccountTreeNode
): Promise<ReportData> {
if (!incomeRoot || !expenseRoot) {
return [];
}
const totalIncome = await this.getTotalNode(
incomeRoot,
t`Total Income (Credit)`

View File

@ -14,6 +14,7 @@ export abstract class Report extends Observable<RawValue> {
filters: Field[] = [];
reportData: ReportData;
usePagination: boolean = false;
shouldRefresh: boolean = false;
abstract loading: boolean;
constructor(fyo: Fyo) {
@ -75,15 +76,15 @@ export abstract class Report extends Observable<RawValue> {
}
if (callPostSet) {
await this.postSet(key);
await this.updateData(key);
}
}
async postSet(key?: string) {
async updateData(key?: string, force?: boolean) {
await this.setDefaultFilters();
this.filters = await this.getFilters();
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 getFilters(): Field[] | Promise<Field[]>;
abstract getColumns(): ColumnField[] | Promise<ColumnField[]>;
abstract setReportData(filter?: string): Promise<void>;
abstract setReportData(filter?: string, force?: boolean): Promise<void>;
}

View File

@ -53,9 +53,9 @@ export class TrialBalance extends AccountReport {
];
}
async setReportData(filter?: string) {
async setReportData(filter?: string, force?: boolean) {
this.loading = true;
if (filter !== 'hideGroupAmounts') {
if (force || filter !== 'hideGroupAmounts') {
await this._setRawData();
}

View File

@ -11,7 +11,7 @@ import { truncate } from 'lodash';
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
import { fyo } from './initFyo';
import router from './router';
import { getErrorMessage } from './utils';
import { getErrorMessage, stringifyCircular } from './utils';
import { MessageDialogOptions, ToastOptions } from './utils/types';
import { showMessageDialog, showToast } from './utils/ui';
@ -30,7 +30,7 @@ async function reportError(errorLogObj: ErrorLog) {
error_name: errorLogObj.name,
message: errorLogObj.message,
stack: errorLogObj.stack,
more: JSON.stringify(errorLogObj.more ?? {}),
more: stringifyCircular(errorLogObj.more ?? {}),
};
if (fyo.store.isDevelopment) {

View File

@ -77,7 +77,7 @@ export default {
let colors = ['#2490EF', '#B7BFC6'];
if (!this.hasData) {
data = dummyData;
colors = ['#E9EBED', '#B7BFC6'];
colors = ['#E9EBED', '#DFE1E2'];
}
const xLabels = data.map((cf) => cf['yearmonth']);

View File

@ -74,7 +74,7 @@ export default defineComponent({
}
if (filterKeys.length) {
await this.report.postSet()
await this.report.postSet();
}
if (fyo.store.isDevelopment) {
@ -117,6 +117,8 @@ export default defineComponent({
if (!this.report.reportData.length) {
await this.report.setReportData();
} else if (this.report.shouldRefresh) {
await this.report.setReportData(undefined, true);
}
},
},

View File

@ -9,7 +9,7 @@ import { fyo } from 'src/initFyo';
export function getDatesAndPeriodList(
period: 'This Year' | 'This Quarter' | 'This Month'
): { periodList: DateTime[]; fromDate: DateTime; toDate: DateTime } {
const toDate: DateTime = DateTime.now();
const toDate: DateTime = DateTime.now().plus({ days: 1 });
let fromDate: DateTime;
if (period === 'This Year') {

View File

@ -417,7 +417,7 @@ export class Search {
_setFilterDefaults() {
const totalChildKeywords = Object.values(this.searchables)
.filter((s) => s.isChild)
.map((s) => this.keywords[s.schemaName].length)
.map((s) => this.keywords[s.schemaName]?.length ?? 0)
.reduce((a, b) => a + b);
if (totalChildKeywords > 2_000) {

View File

@ -7,6 +7,7 @@ import { t } from 'fyo';
import { Doc } from 'fyo/model/doc';
import { Action } from 'fyo/model/types';
import { getActions } from 'fyo/utils';
import { getDbError, LinkValidationError } from 'fyo/utils/errors';
import { ModelNameEnum } from 'models/types';
import { handleErrorWithDialog } from 'src/errorHandling';
import { fyo } from 'src/initFyo';
@ -106,7 +107,7 @@ export async function showToast(options: ToastOptions) {
}
// @ts-ignore
window.st = showToast
window.st = showToast;
function replaceAndAppendMount(app: App<Element>, replaceId: string) {
const fragment = document.createDocumentFragment();
@ -162,7 +163,15 @@ export async function deleteDocWithPrompt(doc: Doc) {
await doc.delete();
return true;
} catch (err) {
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;
}
},