2
0
mirror of https://github.com/frappe/books.git synced 2024-11-09 15:20:56 +00:00

chore: fix all fixable eslint errors in remaining non vue files

- update files to ignore
- delete babel.config.js
This commit is contained in:
18alantom 2023-06-22 14:22:54 +05:30
parent f2b620f256
commit d0571a2450
73 changed files with 503 additions and 510 deletions

View File

@ -28,5 +28,13 @@ module.exports = {
'plugin:@typescript-eslint/recommended', 'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking', 'plugin:@typescript-eslint/recommended-requiring-type-checking',
], ],
ignorePatterns: ['.eslintrc.js', 'tailwind.config.js'], ignorePatterns: [
'.eslintrc.js',
'tailwind.config.js',
'node_modules',
'dist_electron',
'*.spec.ts',
'vite.config.ts',
'postcss.config.js',
],
}; };

View File

@ -1,3 +0,0 @@
module.exports = {
presets: ['@vue/cli-plugin-babel/preset']
};

View File

@ -1,3 +1,10 @@
import {
Cashflow,
IncomeExpense,
TopExpenses,
TotalCreditAndDebit,
TotalOutstanding,
} from 'utils/db/types';
import { ModelNameEnum } from '../../models/types'; import { ModelNameEnum } from '../../models/types';
import DatabaseCore from './core'; import DatabaseCore from './core';
import { BespokeFunction } from './types'; import { BespokeFunction } from './types';
@ -43,7 +50,7 @@ export class BespokeQueries {
.groupBy('account') .groupBy('account')
.orderBy('total', 'desc') .orderBy('total', 'desc')
.limit(5); .limit(5);
return topExpenses; return topExpenses as TopExpenses;
} }
static async getTotalOutstanding( static async getTotalOutstanding(
@ -52,13 +59,13 @@ export class BespokeQueries {
fromDate: string, fromDate: string,
toDate: string toDate: string
) { ) {
return await db.knex!(schemaName) return (await db.knex!(schemaName)
.sum({ total: 'baseGrandTotal' }) .sum({ total: 'baseGrandTotal' })
.sum({ outstanding: 'outstandingAmount' }) .sum({ outstanding: 'outstandingAmount' })
.where('submitted', true) .where('submitted', true)
.where('cancelled', false) .where('cancelled', false)
.whereBetween('date', [fromDate, toDate]) .whereBetween('date', [fromDate, toDate])
.first(); .first()) as TotalOutstanding;
} }
static async getCashflow(db: DatabaseCore, fromDate: string, toDate: string) { static async getCashflow(db: DatabaseCore, fromDate: string, toDate: string) {
@ -67,7 +74,7 @@ export class BespokeQueries {
.where('accountType', 'in', ['Cash', 'Bank']) .where('accountType', 'in', ['Cash', 'Bank'])
.andWhere('isGroup', false); .andWhere('isGroup', false);
const dateAsMonthYear = db.knex!.raw(`strftime('%Y-%m', ??)`, 'date'); const dateAsMonthYear = db.knex!.raw(`strftime('%Y-%m', ??)`, 'date');
return await db.knex!('AccountingLedgerEntry') return (await db.knex!('AccountingLedgerEntry')
.where('reverted', false) .where('reverted', false)
.sum({ .sum({
inflow: 'debit', inflow: 'debit',
@ -78,7 +85,7 @@ export class BespokeQueries {
}) })
.where('account', 'in', cashAndBankAccounts) .where('account', 'in', cashAndBankAccounts)
.whereBetween('date', [fromDate, toDate]) .whereBetween('date', [fromDate, toDate])
.groupBy(dateAsMonthYear); .groupBy(dateAsMonthYear)) as Cashflow;
} }
static async getIncomeAndExpenses( static async getIncomeAndExpenses(
@ -86,7 +93,7 @@ export class BespokeQueries {
fromDate: string, fromDate: string,
toDate: string toDate: string
) { ) {
const income = await db.knex!.raw( const income = (await db.knex!.raw(
` `
select sum(cast(credit as real) - cast(debit as real)) as balance, strftime('%Y-%m', date) as yearmonth select sum(cast(credit as real) - cast(debit as real)) as balance, strftime('%Y-%m', date) as yearmonth
from AccountingLedgerEntry from AccountingLedgerEntry
@ -100,9 +107,9 @@ export class BespokeQueries {
) )
group by yearmonth`, group by yearmonth`,
[fromDate, toDate] [fromDate, toDate]
); )) as IncomeExpense['income'];
const expense = await db.knex!.raw( const expense = (await db.knex!.raw(
` `
select sum(cast(debit as real) - cast(credit as real)) as balance, strftime('%Y-%m', date) as yearmonth select sum(cast(debit as real) - cast(credit as real)) as balance, strftime('%Y-%m', date) as yearmonth
from AccountingLedgerEntry from AccountingLedgerEntry
@ -116,20 +123,20 @@ export class BespokeQueries {
) )
group by yearmonth`, group by yearmonth`,
[fromDate, toDate] [fromDate, toDate]
); )) as IncomeExpense['expense'];
return { income, expense }; return { income, expense };
} }
static async getTotalCreditAndDebit(db: DatabaseCore) { static async getTotalCreditAndDebit(db: DatabaseCore) {
return await db.knex!.raw(` return (await db.knex!.raw(`
select select
account, account,
sum(cast(credit as real)) as totalCredit, sum(cast(credit as real)) as totalCredit,
sum(cast(debit as real)) as totalDebit sum(cast(debit as real)) as totalDebit
from AccountingLedgerEntry from AccountingLedgerEntry
group by account group by account
`); `)) as unknown as TotalCreditAndDebit;
} }
static async getStockQuantity( static async getStockQuantity(
@ -141,6 +148,7 @@ export class BespokeQueries {
batch?: string, batch?: string,
serialNumbers?: string[] serialNumbers?: string[]
): Promise<number | null> { ): Promise<number | null> {
/* eslint-disable @typescript-eslint/no-floating-promises */
const query = db.knex!(ModelNameEnum.StockLedgerEntry) const query = db.knex!(ModelNameEnum.StockLedgerEntry)
.sum('quantity') .sum('quantity')
.where('item', item); .where('item', item);

View File

@ -1,9 +1,4 @@
import { import { getDbError, NotFoundError, ValueError } from 'fyo/utils/errors';
CannotCommitError,
getDbError,
NotFoundError,
ValueError,
} from 'fyo/utils/errors';
import { knex, Knex } from 'knex'; import { knex, Knex } from 'knex';
import { import {
Field, Field,
@ -73,16 +68,16 @@ export default class DatabaseCore extends DatabaseBase {
let query: { value: string }[] = []; let query: { value: string }[] = [];
try { try {
query = await db.knex!('SingleValue').where({ query = (await db.knex!('SingleValue').where({
fieldname: 'countryCode', fieldname: 'countryCode',
parent: 'SystemSettings', parent: 'SystemSettings',
}); })) as { value: string }[];
} catch { } catch {
// Database not inialized and no countryCode passed // Database not inialized and no countryCode passed
} }
if (query.length > 0) { if (query.length > 0) {
countryCode = query[0].value as string; countryCode = query[0].value;
} }
await db.close(); await db.close();
@ -95,8 +90,8 @@ 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) => {
error.type = getDbError(error); error.cause = getDbError(error);
}); });
await this.knex.raw('PRAGMA foreign_keys=ON'); await this.knex.raw('PRAGMA foreign_keys=ON');
} }
@ -105,22 +100,6 @@ export default class DatabaseCore extends DatabaseBase {
await this.knex!.destroy(); await this.knex!.destroy();
} }
async commit() {
/**
* this auto commits, commit is not required
* will later wrap the outermost functions in
* transactions.
*/
try {
// await this.knex!.raw('commit');
} catch (err) {
const type = getDbError(err as Error);
if (type !== CannotCommitError) {
throw err;
}
}
}
async migrate() { async migrate() {
for (const schemaName in this.schemaMap) { for (const schemaName in this.schemaMap) {
const schema = this.schemaMap[schemaName] as Schema; const schema = this.schemaMap[schemaName] as Schema;
@ -135,7 +114,6 @@ export default class DatabaseCore extends DatabaseBase {
} }
} }
await this.commit();
await this.#initializeSingles(); await this.#initializeSingles();
} }
@ -149,6 +127,7 @@ export default class DatabaseCore extends DatabaseBase {
try { try {
const qb = this.knex!(schemaName); const qb = this.knex!(schemaName);
if (name !== undefined) { if (name !== undefined) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
qb.where({ name }); qb.where({ name });
} }
row = await qb.limit(1); row = await qb.limit(1);
@ -178,7 +157,7 @@ export default class DatabaseCore extends DatabaseBase {
async get( async get(
schemaName: string, schemaName: string,
name: string = '', name = '',
fields?: string | string[] fields?: string | string[]
): Promise<FieldValueMap> { ): Promise<FieldValueMap> {
const schema = this.schemaMap[schemaName] as Schema; const schema = this.schemaMap[schemaName] as Schema;
@ -316,7 +295,6 @@ export default class DatabaseCore extends DatabaseBase {
await this.knex!(schemaName) await this.knex!(schemaName)
.update({ name: newName }) .update({ name: newName })
.where('name', oldName); .where('name', oldName);
await this.commit();
} }
async update(schemaName: string, fieldValueMap: FieldValueMap) { async update(schemaName: string, fieldValueMap: FieldValueMap) {
@ -422,6 +400,7 @@ export default class DatabaseCore extends DatabaseBase {
filters: QueryFilter, filters: QueryFilter,
options: GetQueryBuilderOptions options: GetQueryBuilderOptions
): Knex.QueryBuilder { ): Knex.QueryBuilder {
/* eslint-disable @typescript-eslint/no-floating-promises */
const builder = this.knex!.select(fields).from(schemaName); const builder = this.knex!.select(fields).from(schemaName);
this.#applyFiltersToBuilder(builder, filters); this.#applyFiltersToBuilder(builder, filters);
@ -464,12 +443,12 @@ export default class DatabaseCore extends DatabaseBase {
// => `date >= 2017-09-09 and date <= 2017-11-01` // => `date >= 2017-09-09 and date <= 2017-11-01`
const filtersArray = this.#getFiltersArray(filters); const filtersArray = this.#getFiltersArray(filters);
for (const i in filtersArray) { for (let i = 0; i < filtersArray.length; i++) {
const filter = filtersArray[i]; const filter = filtersArray[i];
const field = filter[0] as string; const field = filter[0] as string;
const operator = filter[1]; const operator = filter[1];
const comparisonValue = filter[2]; const comparisonValue = filter[2];
const type = i === '0' ? 'where' : 'andWhere'; const type = i === 0 ? 'where' : 'andWhere';
if (operator === '=') { if (operator === '=') {
builder[type](field, comparisonValue); builder[type](field, comparisonValue);
@ -505,7 +484,8 @@ export default class DatabaseCore extends DatabaseBase {
if ( if (
operator === 'like' && operator === 'like' &&
!(comparisonValue as (string | number)[]).includes('%') typeof comparisonValue === 'string' &&
!comparisonValue.includes('%')
) { ) {
comparisonValue = `%${comparisonValue}%`; comparisonValue = `%${comparisonValue}%`;
} }
@ -595,11 +575,8 @@ export default class DatabaseCore extends DatabaseBase {
} }
// link // link
if ( if (field.fieldtype === FieldTypeEnum.Link && field.target) {
field.fieldtype === FieldTypeEnum.Link && const targetSchemaName = field.target;
(field as TargetField).target
) {
const targetSchemaName = (field as TargetField).target as string;
const schema = this.schemaMap[targetSchemaName] as Schema; const schema = this.schemaMap[targetSchemaName] as Schema;
table table
.foreign(field.fieldname) .foreign(field.fieldname)
@ -630,7 +607,7 @@ export default class DatabaseCore extends DatabaseBase {
} }
if (newForeignKeys.length) { if (newForeignKeys.length) {
await this.#addForeignKeys(schemaName, newForeignKeys); await this.#addForeignKeys(schemaName);
} }
} }
@ -652,15 +629,15 @@ export default class DatabaseCore extends DatabaseBase {
async #getNonExtantSingleValues(singleSchemaName: string) { async #getNonExtantSingleValues(singleSchemaName: string) {
const existingFields = ( const existingFields = (
await this.knex!('SingleValue') (await this.knex!('SingleValue')
.where({ parent: singleSchemaName }) .where({ parent: singleSchemaName })
.select('fieldname') .select('fieldname')) as { fieldname: string }[]
).map(({ fieldname }) => fieldname); ).map(({ fieldname }) => fieldname);
return this.schemaMap[singleSchemaName]!.fields.map( return this.schemaMap[singleSchemaName]!.fields.map(
({ fieldname, default: value }) => ({ ({ fieldname, default: value }) => ({
fieldname, fieldname,
value: value as RawValue | undefined, value: value,
}) })
).filter( ).filter(
({ fieldname, value }) => ({ fieldname, value }) =>
@ -710,7 +687,7 @@ export default class DatabaseCore extends DatabaseBase {
child.idx ??= idx; child.idx ??= idx;
} }
async #addForeignKeys(schemaName: string, newForeignKeys: Field[]) { async #addForeignKeys(schemaName: string) {
const tableRows = await this.knex!.select().from(schemaName); const tableRows = await this.knex!.select().from(schemaName);
await this.prestigeTheTable(schemaName, tableRows); await this.prestigeTheTable(schemaName, tableRows);
} }
@ -731,10 +708,10 @@ export default class DatabaseCore extends DatabaseBase {
} }
async #getOne(schemaName: string, name: string, fields: string[]) { async #getOne(schemaName: string, name: string, fields: string[]) {
const fieldValueMap: FieldValueMap = await this.knex!.select(fields) const fieldValueMap = (await this.knex!.select(fields)
.from(schemaName) .from(schemaName)
.where('name', name) .where('name', name)
.first(); .first()) as FieldValueMap;
return fieldValueMap; return fieldValueMap;
} }
@ -794,9 +771,9 @@ export default class DatabaseCore extends DatabaseBase {
fieldname, fieldname,
}; };
const names: { name: string }[] = await this.knex!('SingleValue') const names = (await this.knex!('SingleValue')
.select('name') .select('name')
.where(updateKey); .where(updateKey)) as { name: string }[];
if (!names?.length) { if (!names?.length) {
this.#insertSingleValue(singleSchemaName, fieldname, value); this.#insertSingleValue(singleSchemaName, fieldname, value);
@ -899,7 +876,7 @@ export default class DatabaseCore extends DatabaseBase {
continue; continue;
} }
for (const child of tableFieldValue!) { for (const child of tableFieldValue) {
this.#prepareChild(schemaName, parentName, child, field, added.length); this.#prepareChild(schemaName, parentName, child, field, added.length);
if ( if (

View File

@ -204,7 +204,7 @@ export async function assertDoesNotThrow(
throw new assert.AssertionError({ throw new assert.AssertionError({
message: `Got unwanted exception${ message: `Got unwanted exception${
message ? `: ${message}` : '' message ? `: ${message}` : ''
}\nError: ${(err as Error).message}\n${(err as Error).stack}`, }\nError: ${(err as Error).message}\n${(err as Error).stack ?? ''}`,
}); });
} }
} }

View File

@ -55,7 +55,13 @@ export function emitMainProcessError(
error: unknown, error: unknown,
more?: Record<string, unknown> more?: Record<string, unknown>
) { ) {
(process.emit as Function)(CUSTOM_EVENTS.MAIN_PROCESS_ERROR, error, more); (
process.emit as (
event: string,
error: unknown,
more?: Record<string, unknown>
) => void
)(CUSTOM_EVENTS.MAIN_PROCESS_ERROR, error, more);
} }
export async function checkFileAccess(filePath: string, mode?: number) { export async function checkFileAccess(filePath: string, mode?: number) {

View File

@ -1,5 +1,6 @@
import { DatabaseManager } from '../database/manager'; import { DatabaseManager } from '../database/manager';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async function execute(dm: DatabaseManager) { async function execute(dm: DatabaseManager) {
/** /**
* Execute function will receive the DatabaseManager which is to be used * Execute function will receive the DatabaseManager which is to be used

View File

@ -30,9 +30,9 @@ async function execute(dm: DatabaseManager) {
const sourceKnex = dm.db!.knex!; const sourceKnex = dm.db!.knex!;
const version = ( const version = (
await sourceKnex('SingleValue') (await sourceKnex('SingleValue')
.select('value') .select('value')
.where({ fieldname: 'version' }) .where({ fieldname: 'version' })) as { value: string }[]
)?.[0]?.value; )?.[0]?.value;
/** /**
@ -58,7 +58,7 @@ async function execute(dm: DatabaseManager) {
await copyData(sourceKnex, destDm); await copyData(sourceKnex, destDm);
} catch (err) { } catch (err) {
const destPath = destDm.db!.dbPath; const destPath = destDm.db!.dbPath;
destDm.db!.close(); await destDm.db!.close();
await fs.unlink(destPath); await fs.unlink(destPath);
throw err; throw err;
} }
@ -94,7 +94,7 @@ async function replaceDatabaseCore(
async function copyData(sourceKnex: Knex, destDm: DatabaseManager) { async function copyData(sourceKnex: Knex, destDm: DatabaseManager) {
const destKnex = destDm.db!.knex!; const destKnex = destDm.db!.knex!;
const schemaMap = destDm.getSchemaMap(); const schemaMap = destDm.getSchemaMap();
await destKnex!.raw('PRAGMA foreign_keys=OFF'); await destKnex.raw('PRAGMA foreign_keys=OFF');
await copySingleValues(sourceKnex, destKnex, schemaMap); await copySingleValues(sourceKnex, destKnex, schemaMap);
await copyParty(sourceKnex, destKnex, schemaMap[ModelNameEnum.Party]!); await copyParty(sourceKnex, destKnex, schemaMap[ModelNameEnum.Party]!);
await copyItem(sourceKnex, destKnex, schemaMap[ModelNameEnum.Item]!); await copyItem(sourceKnex, destKnex, schemaMap[ModelNameEnum.Item]!);
@ -111,7 +111,7 @@ async function copyData(sourceKnex: Knex, destDm: DatabaseManager) {
destKnex, destKnex,
schemaMap[ModelNameEnum.NumberSeries]! schemaMap[ModelNameEnum.NumberSeries]!
); );
await destKnex!.raw('PRAGMA foreign_keys=ON'); await destKnex.raw('PRAGMA foreign_keys=ON');
} }
async function copyNumberSeries( async function copyNumberSeries(
@ -137,14 +137,14 @@ async function copyNumberSeries(
continue; continue;
} }
const indices = await sourceKnex.raw( const indices = (await sourceKnex.raw(
` `
select cast(substr(name, ??) as int) as idx select cast(substr(name, ??) as int) as idx
from ?? from ??
order by idx desc order by idx desc
limit 1`, limit 1`,
[name.length + 1, referenceType] [name.length + 1, referenceType]
); )) as { idx: number }[];
value.start = 1001; value.start = 1001;
value.current = indices[0]?.idx ?? value.current ?? value.start; value.current = indices[0]?.idx ?? value.current ?? value.start;
@ -358,7 +358,9 @@ async function getCountryCode(knex: Knex) {
* Need to account for schema changes, in 0.4.3-beta.0 * Need to account for schema changes, in 0.4.3-beta.0
*/ */
const country = ( const country = (
await knex('SingleValue').select('value').where({ fieldname: 'country' }) (await knex('SingleValue')
.select('value')
.where({ fieldname: 'country' })) as { value: string }[]
)?.[0]?.value; )?.[0]?.value;
if (!country) { if (!country) {

View File

@ -25,8 +25,8 @@ type Notifier = (stage: string, percent: number) => void;
export async function setupDummyInstance( export async function setupDummyInstance(
dbPath: string, dbPath: string,
fyo: Fyo, fyo: Fyo,
years: number = 1, years = 1,
baseCount: number = 1000, baseCount = 1000,
notifier?: Notifier notifier?: Notifier
) { ) {
await fyo.purgeCache(); await fyo.purgeCache();
@ -251,7 +251,7 @@ async function getSalesInvoices(
* For each date create a Sales Invoice. * For each date create a Sales Invoice.
*/ */
for (const d in dates) { for (let d = 0; d < dates.length; d++) {
const date = dates[d]; const date = dates[d];
notifier?.( notifier?.(
@ -424,7 +424,7 @@ async function getSalesPurchaseInvoices(
for (const item of supplierGrouped[supplier]) { for (const item of supplierGrouped[supplier]) {
await doc.append('items', {}); await doc.append('items', {});
const quantity = purchaseQty[item]; const quantity = purchaseQty[item];
doc.items!.at(-1)!.set({ item, quantity }); await doc.items!.at(-1)!.set({ item, quantity });
} }
invoices.push(doc); invoices.push(doc);
@ -527,7 +527,7 @@ async function syncAndSubmit(docs: Doc[], notifier?: Notifier) {
}; };
const total = docs.length; const total = docs.length;
for (const i in docs) { for (let i = 0; i < docs.length; i++) {
const doc = docs[i]; const doc = docs[i];
notifier?.( notifier?.(
`Syncing ${nameMap[doc.schemaName]}, ${i} out of ${total}`, `Syncing ${nameMap[doc.schemaName]}, ${i} out of ${total}`,

View File

@ -62,58 +62,6 @@ export class AuthHandler {
return null; return null;
} }
async login(email: string, password: string) {
if (email === 'Administrator') {
this.#session.user = 'Administrator';
return;
}
const response = await fetch(this.#getServerURL() + '/api/login', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, password }),
});
if (response.status === 200) {
const res = await response.json();
this.#session.user = email;
this.#session.token = res.token;
return res;
}
return response;
}
async signup(email: string, fullName: string, password: string) {
const response = await fetch(this.#getServerURL() + '/api/signup', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, fullName, password }),
});
if (response.status === 200) {
return await response.json();
}
return response;
}
async logout() {
// TODO: Implement this with auth flow
}
#getServerURL() {
return this.#config.serverURL || '';
}
async getCreds(): Promise<Creds> { async getCreds(): Promise<Creds> {
if (!this.#creds) { if (!this.#creds) {
this.#creds = await this.#demux.getCreds(); this.#creds = await this.#demux.getCreds();

View File

@ -144,7 +144,7 @@ export class Converter {
return this.#toRawValueMap(parentSchemaName, value.getValidDict()); return this.#toRawValueMap(parentSchemaName, value.getValidDict());
} }
return this.#toRawValueMap(parentSchemaName, value as DocValueMap); return this.#toRawValueMap(parentSchemaName, value);
}); });
} else { } else {
rawValueMap[fieldname] = Converter.toRawValue( rawValueMap[fieldname] = Converter.toRawValue(
@ -176,7 +176,7 @@ function toDocString(value: RawValue, field: Field) {
} }
function toDocDate(value: RawValue, field: Field) { function toDocDate(value: RawValue, field: Field) {
if ((value as any) instanceof Date) { if ((value as unknown) instanceof Date) {
return value; return value;
} }
@ -286,7 +286,7 @@ function toDocAttachment(value: RawValue, field: Field): null | Attachment {
} }
try { try {
return JSON.parse(value) || null; return (JSON.parse(value) as Attachment) || null;
} catch { } catch {
throwError(value, field, 'doc'); throwError(value, field, 'doc');
} }
@ -322,7 +322,7 @@ function toRawInt(value: DocValue, field: Field): number {
} }
if (typeof value === 'number') { if (typeof value === 'number') {
return Math.floor(value as number); return Math.floor(value);
} }
throwError(value, field, 'raw'); throwError(value, field, 'raw');
@ -363,7 +363,7 @@ function toRawDateTime(value: DocValue, field: Field): string | null {
} }
if (value instanceof Date) { if (value instanceof Date) {
return (value as Date).toISOString(); return value.toISOString();
} }
if (value instanceof DateTime) { if (value instanceof DateTime) {
@ -431,8 +431,8 @@ function toRawAttachment(value: DocValue, field: Field): null | string {
function throwError<T>(value: T, field: Field, type: 'raw' | 'doc'): never { function throwError<T>(value: T, field: Field, type: 'raw' | 'doc'): never {
throw new ValueError( throw new ValueError(
`invalid ${type} conversion '${value}' of type ${typeof value} found, field: ${JSON.stringify( `invalid ${type} conversion '${String(
field value
)}` )}' of type ${typeof value} found, field: ${JSON.stringify(field)}`
); );
} }

View File

@ -7,10 +7,15 @@ import { translateSchema } from 'fyo/utils/translation';
import { Field, RawValue, SchemaMap } from 'schemas/types'; import { Field, RawValue, SchemaMap } from 'schemas/types';
import { getMapFromList } from 'utils'; import { getMapFromList } from 'utils';
import { import {
Cashflow,
DatabaseBase, DatabaseBase,
DatabaseDemuxBase, DatabaseDemuxBase,
GetAllOptions, GetAllOptions,
IncomeExpense,
QueryFilter, QueryFilter,
TopExpenses,
TotalCreditAndDebit,
TotalOutstanding,
} from 'utils/db/types'; } from 'utils/db/types';
import { schemaTranslateables } from 'utils/translationHelpers'; import { schemaTranslateables } from 'utils/translationHelpers';
import { LanguageMap } from 'utils/types'; import { LanguageMap } from 'utils/types';
@ -22,20 +27,10 @@ import {
RawValueMap, RawValueMap,
} from './types'; } from './types';
// Return types of Bespoke Queries
type TopExpenses = { account: string; total: number }[];
type TotalOutstanding = { total: number; outstanding: number };
type Cashflow = { inflow: number; outflow: number; yearmonth: string }[];
type Balance = { balance: number; yearmonth: string }[];
type IncomeExpense = { income: Balance; expense: Balance };
type TotalCreditAndDebit = {
account: string;
totalCredit: number;
totalDebit: number;
};
type FieldMap = Record<string, Record<string, Field>>; type FieldMap = Record<string, Record<string, Field>>;
export class DatabaseHandler extends DatabaseBase { export class DatabaseHandler extends DatabaseBase {
/* eslint-disable @typescript-eslint/no-floating-promises */
#fyo: Fyo; #fyo: Fyo;
converter: Converter; converter: Converter;
#demux: DatabaseDemuxBase; #demux: DatabaseDemuxBase;
@ -83,7 +78,7 @@ export class DatabaseHandler extends DatabaseBase {
} }
async init() { async init() {
this.#schemaMap = (await this.#demux.getSchemaMap()) as SchemaMap; this.#schemaMap = await this.#demux.getSchemaMap();
this.#setFieldMap(); this.#setFieldMap();
this.observer = new Observable(); this.observer = new Observable();
} }
@ -92,7 +87,7 @@ export class DatabaseHandler extends DatabaseBase {
if (languageMap) { if (languageMap) {
translateSchema(this.#schemaMap, languageMap, schemaTranslateables); translateSchema(this.#schemaMap, languageMap, schemaTranslateables);
} else { } else {
this.#schemaMap = (await this.#demux.getSchemaMap()) as SchemaMap; this.#schemaMap = await this.#demux.getSchemaMap();
this.#setFieldMap(); this.#setFieldMap();
} }
} }
@ -142,6 +137,7 @@ export class DatabaseHandler extends DatabaseBase {
options: GetAllOptions = {} options: GetAllOptions = {}
): Promise<DocValueMap[]> { ): Promise<DocValueMap[]> {
const rawValueMap = await this.#getAll(schemaName, options); const rawValueMap = await this.#getAll(schemaName, options);
this.observer.trigger(`getAll:${schemaName}`, options); this.observer.trigger(`getAll:${schemaName}`, options);
return this.converter.toDocValueMap( return this.converter.toDocValueMap(
schemaName, schemaName,
@ -154,6 +150,7 @@ export class DatabaseHandler extends DatabaseBase {
options: GetAllOptions = {} options: GetAllOptions = {}
): Promise<RawValueMap[]> { ): Promise<RawValueMap[]> {
const all = await this.#getAll(schemaName, options); const all = await this.#getAll(schemaName, options);
this.observer.trigger(`getAllRaw:${schemaName}`, options); this.observer.trigger(`getAllRaw:${schemaName}`, options);
return all; return all;
} }
@ -188,6 +185,7 @@ export class DatabaseHandler extends DatabaseBase {
): Promise<number> { ): Promise<number> {
const rawValueMap = await this.#getAll(schemaName, options); const rawValueMap = await this.#getAll(schemaName, options);
const count = rawValueMap.length; const count = rawValueMap.length;
this.observer.trigger(`count:${schemaName}`, options); this.observer.trigger(`count:${schemaName}`, options);
return count; return count;
} }
@ -199,18 +197,21 @@ export class DatabaseHandler extends DatabaseBase {
newName: string newName: string
): Promise<void> { ): Promise<void> {
await this.#demux.call('rename', schemaName, oldName, newName); await this.#demux.call('rename', schemaName, oldName, newName);
this.observer.trigger(`rename:${schemaName}`, { oldName, newName }); this.observer.trigger(`rename:${schemaName}`, { oldName, newName });
} }
async update(schemaName: string, docValueMap: DocValueMap): Promise<void> { async update(schemaName: string, docValueMap: DocValueMap): Promise<void> {
const rawValueMap = this.converter.toRawValueMap(schemaName, docValueMap); const rawValueMap = this.converter.toRawValueMap(schemaName, docValueMap);
await this.#demux.call('update', schemaName, rawValueMap); await this.#demux.call('update', schemaName, rawValueMap);
this.observer.trigger(`update:${schemaName}`, docValueMap); this.observer.trigger(`update:${schemaName}`, docValueMap);
} }
// Delete // Delete
async delete(schemaName: string, name: string): Promise<void> { async delete(schemaName: string, name: string): Promise<void> {
await this.#demux.call('delete', schemaName, name); await this.#demux.call('delete', schemaName, name);
this.observer.trigger(`delete:${schemaName}`, name); this.observer.trigger(`delete:${schemaName}`, name);
} }
@ -220,6 +221,7 @@ export class DatabaseHandler extends DatabaseBase {
schemaName, schemaName,
filters filters
)) as number; )) as number;
this.observer.trigger(`deleteAll:${schemaName}`, filters); this.observer.trigger(`deleteAll:${schemaName}`, filters);
return count; return count;
} }
@ -231,6 +233,7 @@ export class DatabaseHandler extends DatabaseBase {
schemaName, schemaName,
name name
)) as boolean; )) as boolean;
this.observer.trigger(`exists:${schemaName}`, name); this.observer.trigger(`exists:${schemaName}`, name);
return doesExist; return doesExist;
} }

View File

@ -153,6 +153,7 @@ export class DocHandler {
// propagate change to `docs` // propagate change to `docs`
doc.on('change', (params: unknown) => { doc.on('change', (params: unknown) => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.docs.trigger('change', params); this.docs.trigger('change', params);
}); });
@ -167,22 +168,24 @@ export class DocHandler {
} }
#setCacheUpdationListeners(schemaName: string) { #setCacheUpdationListeners(schemaName: string) {
this.fyo.db.observer.on(`delete:${schemaName}`, (name: string) => { this.fyo.db.observer.on(`delete:${schemaName}`, (name) => {
if (typeof name !== 'string') {
return;
}
this.removeFromCache(schemaName, name); this.removeFromCache(schemaName, name);
}); });
this.fyo.db.observer.on( this.fyo.db.observer.on(`rename:${schemaName}`, (names) => {
`rename:${schemaName}`, const { oldName } = names as { oldName: string };
(names: { oldName: string; newName: string }) => { const doc = this.#getFromCache(schemaName, oldName);
const doc = this.#getFromCache(schemaName, names.oldName); if (doc === undefined) {
if (doc === undefined) { return;
return;
}
this.removeFromCache(schemaName, names.oldName);
this.#addToCache(doc);
} }
);
this.removeFromCache(schemaName, oldName);
this.#addToCache(doc);
});
} }
removeFromCache(schemaName: string, name: string) { removeFromCache(schemaName: string, name: string) {

View File

@ -4,7 +4,7 @@ import { Creds } from 'utils/types';
const { ipcRenderer } = require('electron'); const { ipcRenderer } = require('electron');
export class AuthDemux extends AuthDemuxBase { export class AuthDemux extends AuthDemuxBase {
#isElectron: boolean = false; #isElectron = false;
constructor(isElectron: boolean) { constructor(isElectron: boolean) {
super(); super();
this.#isElectron = isElectron; this.#isElectron = isElectron;

View File

@ -6,7 +6,7 @@ import { BackendResponse } from 'utils/ipc/types';
import { IPC_ACTIONS } from 'utils/messages'; import { IPC_ACTIONS } from 'utils/messages';
export class DatabaseDemux extends DatabaseDemuxBase { export class DatabaseDemux extends DatabaseDemuxBase {
#isElectron: boolean = false; #isElectron = false;
constructor(isElectron: boolean) { constructor(isElectron: boolean) {
super(); super();
this.#isElectron = isElectron; this.#isElectron = isElectron;
@ -27,70 +27,76 @@ export class DatabaseDemux extends DatabaseDemuxBase {
} }
async getSchemaMap(): Promise<SchemaMap> { async getSchemaMap(): Promise<SchemaMap> {
if (this.#isElectron) { if (!this.#isElectron) {
return (await this.#handleDBCall(async function dbFunc() { throw new NotImplemented();
return await ipcRenderer.invoke(IPC_ACTIONS.DB_SCHEMA);
})) as SchemaMap;
} }
throw new NotImplemented(); return (await this.#handleDBCall(async () => {
return (await ipcRenderer.invoke(
IPC_ACTIONS.DB_SCHEMA
)) as BackendResponse;
})) as SchemaMap;
} }
async createNewDatabase( async createNewDatabase(
dbPath: string, dbPath: string,
countryCode?: string countryCode?: string
): Promise<string> { ): Promise<string> {
if (this.#isElectron) { if (!this.#isElectron) {
return (await this.#handleDBCall(async function dbFunc() { throw new NotImplemented();
return await ipcRenderer.invoke(
IPC_ACTIONS.DB_CREATE,
dbPath,
countryCode
);
})) as string;
} }
throw new NotImplemented(); return (await this.#handleDBCall(async () => {
return (await ipcRenderer.invoke(
IPC_ACTIONS.DB_CREATE,
dbPath,
countryCode
)) as BackendResponse;
})) as string;
} }
async connectToDatabase( async connectToDatabase(
dbPath: string, dbPath: string,
countryCode?: string countryCode?: string
): Promise<string> { ): Promise<string> {
if (this.#isElectron) { if (!this.#isElectron) {
return (await this.#handleDBCall(async function dbFunc() { throw new NotImplemented();
return await ipcRenderer.invoke(
IPC_ACTIONS.DB_CONNECT,
dbPath,
countryCode
);
})) as string;
} }
throw new NotImplemented(); return (await this.#handleDBCall(async () => {
return (await ipcRenderer.invoke(
IPC_ACTIONS.DB_CONNECT,
dbPath,
countryCode
)) as BackendResponse;
})) as string;
} }
async call(method: DatabaseMethod, ...args: unknown[]): Promise<unknown> { async call(method: DatabaseMethod, ...args: unknown[]): Promise<unknown> {
if (this.#isElectron) { if (!this.#isElectron) {
return (await this.#handleDBCall(async function dbFunc() { throw new NotImplemented();
return await ipcRenderer.invoke(IPC_ACTIONS.DB_CALL, method, ...args);
})) as unknown;
} }
throw new NotImplemented(); return await this.#handleDBCall(async () => {
return (await ipcRenderer.invoke(
IPC_ACTIONS.DB_CALL,
method,
...args
)) as BackendResponse;
});
} }
async callBespoke(method: string, ...args: unknown[]): Promise<unknown> { async callBespoke(method: string, ...args: unknown[]): Promise<unknown> {
if (this.#isElectron) { if (!this.#isElectron) {
return (await this.#handleDBCall(async function dbFunc() { throw new NotImplemented();
return await ipcRenderer.invoke(
IPC_ACTIONS.DB_BESPOKE,
method,
...args
);
})) as unknown;
} }
throw new NotImplemented(); return await this.#handleDBCall(async () => {
return (await ipcRenderer.invoke(
IPC_ACTIONS.DB_BESPOKE,
method,
...args
)) as BackendResponse;
});
} }
} }

View File

@ -164,7 +164,6 @@ export class Fyo {
async close() { async close() {
await this.db.close(); await this.db.close();
await this.auth.logout();
} }
getField(schemaName: string, fieldname: string) { getField(schemaName: string, fieldname: string) {

View File

@ -9,7 +9,6 @@ import {
DynamicLinkField, DynamicLinkField,
Field, Field,
FieldTypeEnum, FieldTypeEnum,
OptionField,
RawValue, RawValue,
Schema, Schema,
TargetField, TargetField,
@ -37,8 +36,8 @@ import {
FormulaMap, FormulaMap,
FormulaReturn, FormulaReturn,
HiddenMap, HiddenMap,
ListsMap,
ListViewSettings, ListViewSettings,
ListsMap,
ReadOnlyMap, ReadOnlyMap,
RequiredMap, RequiredMap,
TreeViewSettings, TreeViewSettings,
@ -47,6 +46,7 @@ import {
import { validateOptions, validateRequired } from './validationFunction'; import { validateOptions, validateRequired } from './validationFunction';
export class Doc extends Observable<DocValue | Doc[]> { export class Doc extends Observable<DocValue | Doc[]> {
/* eslint-disable @typescript-eslint/no-floating-promises */
name?: string; name?: string;
schema: Readonly<Schema>; schema: Readonly<Schema>;
fyo: Fyo; fyo: Fyo;
@ -62,15 +62,15 @@ export class Doc extends Observable<DocValue | Doc[]> {
parentSchemaName?: string; parentSchemaName?: string;
links?: Record<string, Doc>; links?: Record<string, Doc>;
_dirty: boolean = true; _dirty = true;
_notInserted: boolean = true; _notInserted = true;
_syncing = false; _syncing = false;
constructor( constructor(
schema: Schema, schema: Schema,
data: DocValueMap, data: DocValueMap,
fyo: Fyo, fyo: Fyo,
convertToDocValue: boolean = true convertToDocValue = true
) { ) {
super(); super();
this.fyo = markRaw(fyo); this.fyo = markRaw(fyo);
@ -289,10 +289,10 @@ export class Doc extends Observable<DocValue | Doc[]> {
async set( async set(
fieldname: string | DocValueMap, fieldname: string | DocValueMap,
value?: DocValue | Doc[] | DocValueMap[], value?: DocValue | Doc[] | DocValueMap[],
retriggerChildDocApplyChange: boolean = false retriggerChildDocApplyChange = false
): Promise<boolean> { ): Promise<boolean> {
if (typeof fieldname === 'object') { if (typeof fieldname === 'object') {
return await this.setMultiple(fieldname as DocValueMap); return await this.setMultiple(fieldname);
} }
if (!this._canSet(fieldname, value)) { if (!this._canSet(fieldname, value)) {
@ -391,7 +391,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
} }
if (field.fieldtype === FieldTypeEnum.Currency && !isPesa(defaultValue)) { if (field.fieldtype === FieldTypeEnum.Currency && !isPesa(defaultValue)) {
defaultValue = this.fyo.pesa!(defaultValue as string | number); defaultValue = this.fyo.pesa(defaultValue as string | number);
} }
this[field.fieldname] = defaultValue; this[field.fieldname] = defaultValue;
@ -418,7 +418,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
push( push(
fieldname: string, fieldname: string,
docValueMap: Doc | DocValueMap | RawValueMap = {}, docValueMap: Doc | DocValueMap | RawValueMap = {},
convertToDocValue: boolean = false convertToDocValue = false
) { ) {
const childDocs = [ const childDocs = [
(this[fieldname] ?? []) as Doc[], (this[fieldname] ?? []) as Doc[],
@ -449,7 +449,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
_getChildDoc( _getChildDoc(
docValueMap: Doc | DocValueMap | RawValueMap, docValueMap: Doc | DocValueMap | RawValueMap,
fieldname: string, fieldname: string,
convertToDocValue: boolean = false convertToDocValue = false
): Doc { ): Doc {
if (!this.name && this.schema.naming !== 'manual') { if (!this.name && this.schema.naming !== 'manual') {
this.name = this.fyo.doc.getTemporaryName(this.schema); this.name = this.fyo.doc.getTemporaryName(this.schema);
@ -528,7 +528,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
field.fieldtype === FieldTypeEnum.Select || field.fieldtype === FieldTypeEnum.Select ||
field.fieldtype === FieldTypeEnum.AutoComplete field.fieldtype === FieldTypeEnum.AutoComplete
) { ) {
validateOptions(field as OptionField, value as string, this); validateOptions(field, value as string, this);
} }
validateRequired(field, value, this); validateRequired(field, value, this);
@ -544,10 +544,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
await validator(value); await validator(value);
} }
getValidDict( getValidDict(filterMeta = false, filterComputed = false): DocValueMap {
filterMeta: boolean = false,
filterComputed: boolean = false
): DocValueMap {
let fields = this.schema.fields; let fields = this.schema.fields;
if (filterMeta) { if (filterMeta) {
fields = this.schema.fields.filter((f) => !f.meta); fields = this.schema.fields.filter((f) => !f.meta);
@ -639,11 +636,11 @@ export class Doc extends Observable<DocValue | Doc[]> {
async _loadLink(field: Field) { async _loadLink(field: Field) {
if (field.fieldtype === FieldTypeEnum.Link) { if (field.fieldtype === FieldTypeEnum.Link) {
return await this._loadLinkField(field as TargetField); return await this._loadLinkField(field);
} }
if (field.fieldtype === FieldTypeEnum.DynamicLink) { if (field.fieldtype === FieldTypeEnum.DynamicLink) {
return await this._loadDynamicLinkField(field as DynamicLinkField); return await this._loadDynamicLinkField(field);
} }
} }
@ -767,14 +764,13 @@ export class Doc extends Observable<DocValue | Doc[]> {
changedFieldname?: string, changedFieldname?: string,
retriggerChildDocApplyChange?: boolean retriggerChildDocApplyChange?: boolean
): Promise<boolean> { ): Promise<boolean> {
const doc = this;
let changed = await this._callAllTableFieldsApplyFormula(changedFieldname); let changed = await this._callAllTableFieldsApplyFormula(changedFieldname);
changed = changed =
(await this._applyFormulaForFields(doc, changedFieldname)) || changed; (await this._applyFormulaForFields(this, changedFieldname)) || changed;
if (changed && retriggerChildDocApplyChange) { if (changed && retriggerChildDocApplyChange) {
await this._callAllTableFieldsApplyFormula(changedFieldname); await this._callAllTableFieldsApplyFormula(changedFieldname);
await this._applyFormulaForFields(doc, changedFieldname); await this._applyFormulaForFields(this, changedFieldname);
} }
return changed; return changed;
@ -803,7 +799,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
childDocs: Doc[], childDocs: Doc[],
fieldname?: string fieldname?: string
): Promise<boolean> { ): Promise<boolean> {
let changed: boolean = false; let changed = false;
for (const childDoc of childDocs) { for (const childDoc of childDocs) {
if (!childDoc._applyFormula) { if (!childDoc._applyFormula) {
continue; continue;
@ -978,7 +974,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
async trigger(event: string, params?: unknown) { async trigger(event: string, params?: unknown) {
if (this[event]) { if (this[event]) {
await (this[event] as Function)(params); await (this[event] as (args: unknown) => Promise<void>)(params);
} }
await super.trigger(event, params); await super.trigger(event, params);
@ -993,9 +989,9 @@ export class Doc extends Observable<DocValue | Doc[]> {
try { try {
return this.fyo.pesa(value as string | number); return this.fyo.pesa(value as string | number);
} catch (err) { } catch (err) {
( (err as Error).message += ` value: '${String(
err as Error value
).message += ` value: '${value}' of type: ${typeof value}, fieldname: '${tablefield}', childfield: '${childfield}'`; )}' of type: ${typeof value}, fieldname: '${tablefield}', childfield: '${childfield}'`;
throw err; throw err;
} }
} }
@ -1032,7 +1028,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
if (this.numberSeries) { if (this.numberSeries) {
delete updateMap.name; delete updateMap.name;
} else { } else {
updateMap.name = updateMap.name + ' CPY'; updateMap.name = String(updateMap.name) + ' CPY';
} }
const rawUpdateMap = this.fyo.db.converter.toRawValueMap( const rawUpdateMap = this.fyo.db.converter.toRawValueMap(
@ -1054,6 +1050,8 @@ export class Doc extends Observable<DocValue | Doc[]> {
* *
* This may cause the lifecycle function to execute incorrectly. * This may cause the lifecycle function to execute incorrectly.
*/ */
/* eslint-disable @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars */
async change(ch: ChangeArg) {} async change(ch: ChangeArg) {}
async validate() {} async validate() {}
async beforeSync() {} async beforeSync() {}
@ -1084,7 +1082,9 @@ export class Doc extends Observable<DocValue | Doc[]> {
return {}; return {};
} }
static getTreeSettings(fyo: Fyo): TreeViewSettings | void {} static getTreeSettings(fyo: Fyo): TreeViewSettings | void {
return;
}
static getActions(fyo: Fyo): Action[] { static getActions(fyo: Fyo): Action[] {
return []; return [];

View File

@ -99,23 +99,18 @@ async function getNotFoundDetailsIfDoesNotExists(
): Promise<NotFoundDetails | null> { ): Promise<NotFoundDetails | null> {
const value = doc.get(field.fieldname); const value = doc.get(field.fieldname);
if (field.fieldtype === FieldTypeEnum.Link && value) { if (field.fieldtype === FieldTypeEnum.Link && value) {
return getNotFoundLinkDetails(field as TargetField, value as string, fyo); return getNotFoundLinkDetails(field, value as string, fyo);
} }
if (field.fieldtype === FieldTypeEnum.DynamicLink && value) { if (field.fieldtype === FieldTypeEnum.DynamicLink && value) {
return getNotFoundDynamicLinkDetails( return getNotFoundDynamicLinkDetails(field, value as string, fyo, doc);
field as DynamicLinkField,
value as string,
fyo,
doc
);
} }
if ( if (
field.fieldtype === FieldTypeEnum.Table && field.fieldtype === FieldTypeEnum.Table &&
(value as Doc[] | undefined)?.length (value as Doc[] | undefined)?.length
) { ) {
return getNotFoundTableDetails(value as Doc[], fyo); return await getNotFoundTableDetails(value as Doc[], fyo);
} }
return null; return null;
@ -127,7 +122,7 @@ async function getNotFoundLinkDetails(
fyo: Fyo fyo: Fyo
): Promise<NotFoundDetails | null> { ): Promise<NotFoundDetails | null> {
const { target } = field; const { target } = field;
const exists = await fyo.db.exists(target as string, value); const exists = await fyo.db.exists(target, value);
if (!exists) { if (!exists) {
return { label: field.label, value }; return { label: field.label, value };
} }
@ -160,7 +155,7 @@ async function getNotFoundTableDetails(
fyo: Fyo fyo: Fyo
): Promise<NotFoundDetails | null> { ): Promise<NotFoundDetails | null> {
for (const childDoc of value) { for (const childDoc of value) {
const details = getNotFoundDetails(childDoc, fyo); const details = await getNotFoundDetails(childDoc, fyo);
if (details) { if (details) {
return details; return details;
} }

View File

@ -34,7 +34,7 @@ export function getPreDefaultValues(
case FieldTypeEnum.Table: case FieldTypeEnum.Table:
return [] as Doc[]; return [] as Doc[];
case FieldTypeEnum.Currency: case FieldTypeEnum.Currency:
return fyo.pesa!(0.0); return fyo.pesa(0.0);
case FieldTypeEnum.Int: case FieldTypeEnum.Int:
case FieldTypeEnum.Float: case FieldTypeEnum.Float:
return 0; return 0;
@ -145,9 +145,9 @@ export function isDocValueTruthy(docValue: DocValue | Doc[]) {
} }
export function setChildDocIdx(childDocs: Doc[]) { export function setChildDocIdx(childDocs: Doc[]) {
for (const idx in childDocs) { childDocs.forEach((cd, idx) => {
childDocs[idx].idx = +idx; cd.idx = idx;
} });
} }
export function getFormulaSequence(formulas: FormulaMap) { export function getFormulaSequence(formulas: FormulaMap) {

View File

@ -7,14 +7,26 @@ import { getIsNullOrUndef } from 'utils';
import { Doc } from './doc'; import { Doc } from './doc';
export function validateEmail(value: DocValue) { export function validateEmail(value: DocValue) {
const isValid = /(.+)@(.+){2,}\.(.+){2,}/.test(value as string); if (typeof value !== 'string') {
throw new TypeError(
`Invalid email ${String(value)} of type ${typeof value}`
);
}
const isValid = /(.+)@(.+){2,}\.(.+){2,}/.test(value);
if (!isValid) { if (!isValid) {
throw new ValidationError(`Invalid email: ${value}`); throw new ValidationError(`Invalid email: ${value}`);
} }
} }
export function validatePhoneNumber(value: DocValue) { export function validatePhoneNumber(value: DocValue) {
const isValid = /[+]{0,1}[\d ]+/.test(value as string); if (typeof value !== 'string') {
throw new TypeError(
`Invalid phone ${String(value)} of type ${typeof value}`
);
}
const isValid = /[+]{0,1}[\d ]+/.test(value);
if (!isValid) { if (!isValid) {
throw new ValidationError(`Invalid phone: ${value}`); throw new ValidationError(`Invalid phone: ${value}`);
} }

View File

@ -18,7 +18,7 @@ export default class SystemSettings extends Doc {
instanceId?: string; instanceId?: string;
validations: ValidationMap = { validations: ValidationMap = {
async displayPrecision(value: DocValue) { displayPrecision(value: DocValue) {
if ((value as number) >= 0 && (value as number) <= 9) { if ((value as number) >= 0 && (value as number) <= 9) {
return; return;
} }
@ -38,7 +38,7 @@ export default class SystemSettings extends Doc {
(c) => (c) =>
({ ({
value: countryInfo[c]?.locale, value: countryInfo[c]?.locale,
label: `${c} (${countryInfo[c]?.locale})`, label: `${c} (${countryInfo[c]?.locale ?? t`Not Found`})`,
} as SelectOption) } as SelectOption)
); );
}, },

View File

@ -66,6 +66,7 @@ export class TelemetryManager {
log(verb: Verb, noun: Noun, more?: Record<string, unknown>) { log(verb: Verb, noun: Noun, more?: Record<string, unknown>) {
if (!this.#started && this.fyo.db.isConnected) { if (!this.#started && this.fyo.db.isConnected) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.start().then(() => this.#sendBeacon(verb, noun, more)); this.start().then(() => this.#sendBeacon(verb, noun, more));
return; return;
} }

View File

@ -2,6 +2,7 @@ import { AuthDemuxBase } from 'utils/auth/types';
import { Creds } from 'utils/types'; import { Creds } from 'utils/types';
export class DummyAuthDemux extends AuthDemuxBase { export class DummyAuthDemux extends AuthDemuxBase {
// eslint-disable-next-line @typescript-eslint/require-await
async getCreds(): Promise<Creds> { async getCreds(): Promise<Creds> {
return { errorLogUrl: '', tokenString: '', telemetryUrl: '' }; return { errorLogUrl: '', tokenString: '', telemetryUrl: '' };
} }

View File

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

View File

@ -60,7 +60,7 @@ function toDatetime(value: unknown): DateTime | null {
} else if (value instanceof Date) { } else if (value instanceof Date) {
return DateTime.fromJSDate(value); return DateTime.fromJSDate(value);
} else if (typeof value === 'number') { } else if (typeof value === 'number') {
return DateTime.fromSeconds(value as number); return DateTime.fromSeconds(value);
} }
return null; return null;
@ -120,7 +120,9 @@ function formatCurrency(
try { try {
valueString = formatNumber(value, fyo); valueString = formatNumber(value, fyo);
} catch (err) { } catch (err) {
(err as Error).message += ` value: '${value}', type: ${typeof value}`; (err as Error).message += ` value: '${String(
value
)}', type: ${typeof value}`;
throw err; throw err;
} }
@ -148,7 +150,9 @@ function formatNumber(value: unknown, fyo: Fyo): string {
if (formattedNumber === 'NaN') { if (formattedNumber === 'NaN') {
throw Error( throw Error(
`invalid value passed to formatNumber: '${value}' of type ${typeof value}` `invalid value passed to formatNumber: '${String(
value
)}' of type ${typeof value}`
); );
} }

View File

@ -8,7 +8,7 @@ import { getIsNullOrUndef, safeParseInt } from 'utils';
export function slug(str: string) { export function slug(str: string) {
return str return str
.replace(/(?:^\w|[A-Z]|\b\w)/g, function (letter, index) { .replace(/(?:^\w|[A-Z]|\b\w)/g, (letter, index) => {
return index == 0 ? letter.toLowerCase() : letter.toUpperCase(); return index == 0 ? letter.toLowerCase() : letter.toUpperCase();
}) })
.replace(/\s+/g, ''); .replace(/\s+/g, '');
@ -24,7 +24,7 @@ export function unique<T>(list: T[], key = (it: T) => String(it)) {
export function getDuplicates(array: unknown[]) { export function getDuplicates(array: unknown[]) {
const duplicates: unknown[] = []; const duplicates: unknown[] = [];
for (const i in array) { for (let i = 0; i < array.length; i++) {
const previous = array[safeParseInt(i) - 1]; const previous = array[safeParseInt(i) - 1];
const current = array[i]; const current = array[i];
@ -118,7 +118,7 @@ function getRawOptionList(field: Field, doc: Doc | undefined | null) {
return []; return [];
} }
const Model = doc!.fyo.models[doc!.schemaName]; const Model = doc.fyo.models[doc.schemaName];
if (Model === undefined) { if (Model === undefined) {
return []; return [];
} }
@ -128,7 +128,7 @@ function getRawOptionList(field: Field, doc: Doc | undefined | null) {
return []; return [];
} }
return getList(doc!); return getList(doc);
} }
export function getEmptyValuesByFieldTypes( export function getEmptyValuesByFieldTypes(

View File

@ -3,13 +3,15 @@ enum EventType {
OnceListeners = '_onceListeners', OnceListeners = '_onceListeners',
} }
type Listener = (...args: unknown[]) => unknown | Promise<unknown>;
export default class Observable<T> { export default class Observable<T> {
[key: string]: unknown | T; [key: string]: unknown | T;
_isHot: Map<string, boolean>; _isHot: Map<string, boolean>;
_eventQueue: Map<string, unknown[]>; _eventQueue: Map<string, unknown[]>;
_map: Map<string, unknown>; _map: Map<string, unknown>;
_listeners: Map<string, Function[]>; _listeners: Map<string, Listener[]>;
_onceListeners: Map<string, Function[]>; _onceListeners: Map<string, Listener[]>;
constructor() { constructor() {
this._map = new Map(); this._map = new Map();
@ -37,6 +39,7 @@ export default class Observable<T> {
*/ */
set(key: string, value: T) { set(key: string, value: T) {
this[key] = value; this[key] = value;
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.trigger('change', { this.trigger('change', {
doc: this, doc: this,
changed: key, changed: key,
@ -50,7 +53,7 @@ export default class Observable<T> {
* @param event : name of the event for which the listener is checked * @param event : name of the event for which the listener is checked
* @param listener : specific listener that is checked for * @param listener : specific listener that is checked for
*/ */
hasListener(event: string, listener?: Function) { hasListener(event: string, listener?: Listener) {
const listeners = this[EventType.Listeners].get(event) ?? []; const listeners = this[EventType.Listeners].get(event) ?? [];
const onceListeners = this[EventType.OnceListeners].get(event) ?? []; const onceListeners = this[EventType.OnceListeners].get(event) ?? [];
@ -69,7 +72,7 @@ export default class Observable<T> {
* @param event : name of the event for which the listener is set * @param event : name of the event for which the listener is set
* @param listener : listener that is executed when the event is triggered * @param listener : listener that is executed when the event is triggered
*/ */
on(event: string, listener: Function) { on(event: string, listener: Listener) {
this._addListener(EventType.Listeners, event, listener); this._addListener(EventType.Listeners, event, listener);
} }
@ -80,7 +83,7 @@ export default class Observable<T> {
* @param event : name of the event for which the listener is set * @param event : name of the event for which the listener is set
* @param listener : listener that is executed when the event is triggered * @param listener : listener that is executed when the event is triggered
*/ */
once(event: string, listener: Function) { once(event: string, listener: Listener) {
this._addListener(EventType.OnceListeners, event, listener); this._addListener(EventType.OnceListeners, event, listener);
} }
@ -90,7 +93,7 @@ export default class Observable<T> {
* @param event : name of the event from which to remove the listener * @param event : name of the event from which to remove the listener
* @param listener : listener that was set for the event * @param listener : listener that was set for the event
*/ */
off(event: string, listener: Function) { off(event: string, listener: Listener) {
this._removeListener(EventType.Listeners, event, listener); this._removeListener(EventType.Listeners, event, listener);
this._removeListener(EventType.OnceListeners, event, listener); this._removeListener(EventType.OnceListeners, event, listener);
} }
@ -111,7 +114,7 @@ export default class Observable<T> {
* @param throttle : wait time before triggering the event. * @param throttle : wait time before triggering the event.
*/ */
async trigger(event: string, params?: unknown, throttle: number = 0) { async trigger(event: string, params?: unknown, throttle = 0) {
let isHot = false; let isHot = false;
if (throttle > 0) { if (throttle > 0) {
isHot = this._throttled(event, params, throttle); isHot = this._throttled(event, params, throttle);
@ -125,7 +128,7 @@ export default class Observable<T> {
await this._executeTriggers(event, params); await this._executeTriggers(event, params);
} }
_removeListener(type: EventType, event: string, listener: Function) { _removeListener(type: EventType, event: string, listener: Listener) {
const listeners = (this[type].get(event) ?? []).filter( const listeners = (this[type].get(event) ?? []).filter(
(l) => l !== listener (l) => l !== listener
); );
@ -160,6 +163,7 @@ export default class Observable<T> {
const params = this._eventQueue.get(event); const params = this._eventQueue.get(event);
if (params !== undefined) { if (params !== undefined) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this._executeTriggers(event, params); this._executeTriggers(event, params);
this._eventQueue.delete(event); this._eventQueue.delete(event);
} }
@ -168,7 +172,7 @@ export default class Observable<T> {
return false; return false;
} }
_addListener(type: EventType, event: string, listener: Function) { _addListener(type: EventType, event: string, listener: Listener) {
this._initLiseners(type, event); this._initLiseners(type, event);
const list = this[type].get(event)!; const list = this[type].get(event)!;
if (list.includes(listener)) { if (list.includes(listener)) {

View File

@ -31,7 +31,7 @@ class TranslationString {
} }
#formatArg(arg: string | number | boolean) { #formatArg(arg: string | number | boolean) {
return arg ?? ''; return String(arg ?? '');
} }
#translate() { #translate() {
@ -48,22 +48,23 @@ class TranslationString {
} }
#stitch() { #stitch() {
if (!((this.args[0] as any) instanceof Array)) { if (!((this.args[0] as unknown) instanceof Array)) {
throw new ValueError( throw new ValueError(
`invalid args passed to TranslationString ${ `invalid args passed to TranslationString ${String(
this.args this.args
} of type ${typeof this.args[0]}` )} of type ${typeof this.args[0]}`
); );
} }
this.strList = this.args[0] as any as string[]; this.strList = this.args[0] as unknown as string[];
this.argList = this.args.slice(1) as TranslationArgs[]; this.argList = this.args.slice(1) as TranslationArgs[];
if (this.languageMap) { if (this.languageMap) {
this.#translate(); this.#translate();
} }
return this.strList!.map((s, i) => s + this.#formatArg(this.argList![i])) return this.strList
.map((s, i) => s + this.#formatArg(this.argList![i]))
.join('') .join('')
.replace(/\s+/g, ' ') .replace(/\s+/g, ' ')
.trim(); .trim();

View File

@ -71,9 +71,9 @@ export class LedgerPosting {
const roundOffAccount = await this._getRoundOffAccount(); const roundOffAccount = await this._getRoundOffAccount();
if (difference.gt(0)) { if (difference.gt(0)) {
this.credit(roundOffAccount, absoluteValue); await this.credit(roundOffAccount, absoluteValue);
} else { } else {
this.debit(roundOffAccount, absoluteValue); await this.debit(roundOffAccount, absoluteValue);
} }
} }

View File

@ -59,10 +59,7 @@ export class Account extends Doc {
return; return;
} }
const account = await this.fyo.db.get( const account = await this.fyo.db.get('Account', this.parentAccount);
'Account',
this.parentAccount as string
);
this.accountType = account.accountType as AccountType; this.accountType = account.accountType as AccountType;
} }

View File

@ -1,10 +1,10 @@
import { Fyo, t } from 'fyo'; import { t } from 'fyo';
import { Doc } from 'fyo/model/doc'; import { Doc } from 'fyo/model/doc';
import { import {
EmptyMessageMap, EmptyMessageMap,
FormulaMap, FormulaMap,
ListsMap,
ListViewSettings, ListViewSettings,
ListsMap,
} from 'fyo/model/types'; } from 'fyo/model/types';
import { codeStateMap } from 'regional/in'; import { codeStateMap } from 'regional/in';
import { getCountryInfo } from 'utils/misc'; import { getCountryInfo } from 'utils/misc';
@ -12,7 +12,7 @@ import { getCountryInfo } from 'utils/misc';
export class Address extends Doc { export class Address extends Doc {
formulas: FormulaMap = { formulas: FormulaMap = {
addressDisplay: { addressDisplay: {
formula: async () => { formula: () => {
return [ return [
this.addressLine1, this.addressLine1,
this.addressLine2, this.addressLine2,

View File

@ -143,7 +143,7 @@ export abstract class Invoice extends Transactional {
outstandingAmount: this.baseGrandTotal!, outstandingAmount: this.baseGrandTotal!,
}); });
const party = (await this.fyo.doc.getDoc('Party', this.party!)) as Party; const party = (await this.fyo.doc.getDoc('Party', this.party)) as Party;
await party.updateOutstandingAmount(); await party.updateOutstandingAmount();
if (this.makeAutoPayment && this.autoPaymentAccount) { if (this.makeAutoPayment && this.autoPaymentAccount) {
@ -181,7 +181,7 @@ export abstract class Invoice extends Transactional {
async _updatePartyOutStanding() { async _updatePartyOutStanding() {
const partyDoc = (await this.fyo.doc.getDoc( const partyDoc = (await this.fyo.doc.getDoc(
ModelNameEnum.Party, ModelNameEnum.Party,
this.party! this.party
)) as Party; )) as Party;
await partyDoc.updateOutstandingAmount(); await partyDoc.updateOutstandingAmount();
@ -223,7 +223,7 @@ export abstract class Invoice extends Transactional {
return 1.0; return 1.0;
} }
const exchangeRate = await getExchangeRate({ const exchangeRate = await getExchangeRate({
fromCurrency: this.currency!, fromCurrency: this.currency,
toCurrency: currency as string, toCurrency: currency as string,
}); });
@ -247,7 +247,7 @@ export abstract class Invoice extends Transactional {
continue; continue;
} }
const tax = await this.getTax(item.tax!); const tax = await this.getTax(item.tax);
for (const { account, rate } of (tax.details ?? []) as TaxDetail[]) { for (const { account, rate } of (tax.details ?? []) as TaxDetail[]) {
taxes[account] ??= { taxes[account] ??= {
account, account,
@ -256,7 +256,11 @@ export abstract class Invoice extends Transactional {
}; };
let amount = item.amount!; let amount = item.amount!;
if (this.enableDiscounting && !this.discountAfterTax && !item.itemDiscountedTotal?.isZero()) { if (
this.enableDiscounting &&
!this.discountAfterTax &&
!item.itemDiscountedTotal?.isZero()
) {
amount = item.itemDiscountedTotal!; amount = item.itemDiscountedTotal!;
} }
@ -285,7 +289,7 @@ export abstract class Invoice extends Transactional {
} }
async getTax(tax: string) { async getTax(tax: string) {
if (!this._taxes![tax]) { if (!this._taxes[tax]) {
this._taxes[tax] = await this.fyo.doc.getDoc('Tax', tax); this._taxes[tax] = await this.fyo.doc.getDoc('Tax', tax);
} }
@ -302,7 +306,7 @@ export abstract class Invoice extends Transactional {
return itemDiscountAmount.add(invoiceDiscountAmount); return itemDiscountAmount.add(invoiceDiscountAmount);
} }
async getGrandTotal() { getGrandTotal() {
const totalDiscount = this.getTotalDiscount(); const totalDiscount = this.getTotalDiscount();
return ((this.taxes ?? []) as Doc[]) return ((this.taxes ?? []) as Doc[])
.map((doc) => doc.amount as Money) .map((doc) => doc.amount as Money)
@ -407,16 +411,15 @@ export abstract class Invoice extends Transactional {
}, },
dependsOn: ['party', 'currency'], dependsOn: ['party', 'currency'],
}, },
netTotal: { formula: async () => this.getSum('items', 'amount', false) }, netTotal: { formula: () => this.getSum('items', 'amount', false) },
taxes: { formula: async () => await this.getTaxSummary() }, taxes: { formula: async () => await this.getTaxSummary() },
grandTotal: { formula: async () => await this.getGrandTotal() }, grandTotal: { formula: () => this.getGrandTotal() },
baseGrandTotal: { baseGrandTotal: {
formula: async () => formula: () => (this.grandTotal as Money).mul(this.exchangeRate! ?? 1),
(this.grandTotal as Money).mul(this.exchangeRate! ?? 1),
dependsOn: ['grandTotal', 'exchangeRate'], dependsOn: ['grandTotal', 'exchangeRate'],
}, },
outstandingAmount: { outstandingAmount: {
formula: async () => { formula: () => {
if (this.submitted) { if (this.submitted) {
return; return;
} }
@ -425,7 +428,7 @@ export abstract class Invoice extends Transactional {
}, },
}, },
stockNotTransferred: { stockNotTransferred: {
formula: async () => { formula: () => {
if (this.submitted) { if (this.submitted) {
return; return;
} }
@ -526,7 +529,7 @@ export abstract class Invoice extends Transactional {
!!doc.autoStockTransferLocation, !!doc.autoStockTransferLocation,
numberSeries: (doc) => getNumberSeries(doc.schemaName, doc.fyo), numberSeries: (doc) => getNumberSeries(doc.schemaName, doc.fyo),
terms: (doc) => { terms: (doc) => {
const defaults = doc.fyo.singles.Defaults as Defaults | undefined; const defaults = doc.fyo.singles.Defaults;
if (doc.schemaName === ModelNameEnum.SalesInvoice) { if (doc.schemaName === ModelNameEnum.SalesInvoice) {
return defaults?.salesInvoiceTerms ?? ''; return defaults?.salesInvoiceTerms ?? '';
} }
@ -616,9 +619,7 @@ export abstract class Invoice extends Transactional {
return this.fyo.doc.getNewDoc(ModelNameEnum.Payment, data) as Payment; return this.fyo.doc.getNewDoc(ModelNameEnum.Payment, data) as Payment;
} }
async getStockTransfer( async getStockTransfer(isAuto = false): Promise<StockTransfer | null> {
isAuto: boolean = false
): Promise<StockTransfer | null> {
if (!this.isSubmitted) { if (!this.isSubmitted) {
return null; return null;
} }

View File

@ -188,7 +188,7 @@ export abstract class InvoiceItem extends Doc {
dependsOn: ['item', 'unit'], dependsOn: ['item', 'unit'],
}, },
transferQuantity: { transferQuantity: {
formula: async (fieldname) => { formula: (fieldname) => {
if (fieldname === 'quantity' || this.unit === this.transferUnit) { if (fieldname === 'quantity' || this.unit === this.transferUnit) {
return this.quantity; return this.quantity;
} }
@ -205,7 +205,7 @@ export abstract class InvoiceItem extends Doc {
const itemDoc = await this.fyo.doc.getDoc( const itemDoc = await this.fyo.doc.getDoc(
ModelNameEnum.Item, ModelNameEnum.Item,
this.item as string this.item
); );
const unitDoc = itemDoc.getLink('uom'); const unitDoc = itemDoc.getLink('uom');
@ -321,7 +321,7 @@ export abstract class InvoiceItem extends Doc {
], ],
}, },
itemTaxedTotal: { itemTaxedTotal: {
formula: async (fieldname) => { formula: async () => {
const totalTaxRate = await this.getTotalTaxRate(); const totalTaxRate = await this.getTotalTaxRate();
const rate = this.rate ?? this.fyo.pesa(0); const rate = this.rate ?? this.fyo.pesa(0);
const quantity = this.quantity ?? 1; const quantity = this.quantity ?? 1;
@ -389,7 +389,7 @@ export abstract class InvoiceItem extends Doc {
}; };
validations: ValidationMap = { validations: ValidationMap = {
rate: async (value: DocValue) => { rate: (value: DocValue) => {
if ((value as Money).gte(0)) { if ((value as Money).gte(0)) {
return; return;
} }
@ -401,7 +401,7 @@ export abstract class InvoiceItem extends Doc {
)}) cannot be less zero.` )}) cannot be less zero.`
); );
}, },
itemDiscountAmount: async (value: DocValue) => { itemDiscountAmount: (value: DocValue) => {
if ((value as Money).lte(this.amount!)) { if ((value as Money).lte(this.amount!)) {
return; return;
} }
@ -416,7 +416,7 @@ export abstract class InvoiceItem extends Doc {
)}).` )}).`
); );
}, },
itemDiscountPercent: async (value: DocValue) => { itemDiscountPercent: (value: DocValue) => {
if ((value as number) < 100) { if ((value as number) < 100) {
return; return;
} }
@ -434,13 +434,14 @@ export abstract class InvoiceItem extends Doc {
const item = await this.fyo.db.getAll(ModelNameEnum.UOMConversionItem, { const item = await this.fyo.db.getAll(ModelNameEnum.UOMConversionItem, {
fields: ['parent'], fields: ['parent'],
filters: { uom: value as string, parent: this.item! }, filters: { uom: value as string, parent: this.item },
}); });
if (item.length < 1) if (item.length < 1)
throw new ValidationError( throw new ValidationError(
t`Transfer Unit ${value as string} is not applicable for Item ${this t`Transfer Unit ${value as string} is not applicable for Item ${
.item!}` this.item
}`
); );
}, },
}; };

View File

@ -71,7 +71,7 @@ export class Item extends Doc {
}; };
validations: ValidationMap = { validations: ValidationMap = {
rate: async (value: DocValue) => { rate: (value: DocValue) => {
if ((value as Money).isNegative()) { if ((value as Money).isNegative()) {
throw new ValidationError(this.fyo.t`Rate can't be negative.`); throw new ValidationError(this.fyo.t`Rate can't be negative.`);
} }
@ -85,13 +85,13 @@ export class Item extends Doc {
label: fyo.t`Sales Invoice`, label: fyo.t`Sales Invoice`,
condition: (doc) => !doc.notInserted && doc.for !== 'Purchases', condition: (doc) => !doc.notInserted && doc.for !== 'Purchases',
action: async (doc, router) => { action: async (doc, router) => {
const invoice = await fyo.doc.getNewDoc('SalesInvoice'); const invoice = fyo.doc.getNewDoc('SalesInvoice');
await invoice.append('items', { await invoice.append('items', {
item: doc.name as string, item: doc.name as string,
rate: doc.rate as Money, rate: doc.rate as Money,
tax: doc.tax as string, tax: doc.tax as string,
}); });
router.push(`/edit/SalesInvoice/${invoice.name}`); await router.push(`/edit/SalesInvoice/${invoice.name!}`);
}, },
}, },
{ {
@ -99,13 +99,13 @@ export class Item extends Doc {
label: fyo.t`Purchase Invoice`, label: fyo.t`Purchase Invoice`,
condition: (doc) => !doc.notInserted && doc.for !== 'Sales', condition: (doc) => !doc.notInserted && doc.for !== 'Sales',
action: async (doc, router) => { action: async (doc, router) => {
const invoice = await fyo.doc.getNewDoc('PurchaseInvoice'); const invoice = fyo.doc.getNewDoc('PurchaseInvoice');
await invoice.append('items', { await invoice.append('items', {
item: doc.name as string, item: doc.name as string,
rate: doc.rate as Money, rate: doc.rate as Money,
tax: doc.tax as string, tax: doc.tax as string,
}); });
router.push(`/edit/PurchaseInvoice/${invoice.name}`); await router.push(`/edit/PurchaseInvoice/${invoice.name!}`);
}, },
}, },
]; ];
@ -126,7 +126,9 @@ export class Item extends Doc {
hasBatch: () => hasBatch: () =>
!(this.fyo.singles.InventorySettings?.enableBatches && this.trackItem), !(this.fyo.singles.InventorySettings?.enableBatches && this.trackItem),
hasSerialNumber: () => hasSerialNumber: () =>
!(this.fyo.singles.InventorySettings?.enableSerialNumber && this.trackItem), !(
this.fyo.singles.InventorySettings?.enableSerialNumber && this.trackItem
),
uomConversions: () => uomConversions: () =>
!this.fyo.singles.InventorySettings?.enableUomConversions, !this.fyo.singles.InventorySettings?.enableUomConversions,
}; };

View File

@ -29,10 +29,10 @@ export class JournalEntryAccount extends Doc {
formulas: FormulaMap = { formulas: FormulaMap = {
debit: { debit: {
formula: async () => this.getAutoDebitCredit('debit'), formula: () => this.getAutoDebitCredit('debit'),
}, },
credit: { credit: {
formula: async () => this.getAutoDebitCredit('credit'), formula: () => this.getAutoDebitCredit('credit'),
}, },
}; };

View File

@ -89,7 +89,7 @@ export class Party extends Doc {
dependsOn: ['role'], dependsOn: ['role'],
}, },
currency: { currency: {
formula: async () => { formula: () => {
if (!this.currency) { if (!this.currency) {
return this.fyo.singles.SystemSettings!.currency as string; return this.fyo.singles.SystemSettings!.currency as string;
} }
@ -132,12 +132,13 @@ export class Party extends Doc {
condition: (doc: Doc) => condition: (doc: Doc) =>
!doc.notInserted && (doc.role as PartyRole) !== 'Customer', !doc.notInserted && (doc.role as PartyRole) !== 'Customer',
action: async (partyDoc, router) => { action: async (partyDoc, router) => {
const doc = await fyo.doc.getNewDoc('PurchaseInvoice', { const doc = fyo.doc.getNewDoc('PurchaseInvoice', {
party: partyDoc.name, party: partyDoc.name,
account: partyDoc.defaultAccount as string, account: partyDoc.defaultAccount as string,
}); });
router.push({
path: `/edit/PurchaseInvoice/${doc.name}`, await router.push({
path: `/edit/PurchaseInvoice/${doc.name!}`,
query: { query: {
schemaName: 'PurchaseInvoice', schemaName: 'PurchaseInvoice',
values: { values: {
@ -170,7 +171,7 @@ export class Party extends Doc {
}); });
await router.push({ await router.push({
path: `/edit/SalesInvoice/${doc.name}`, path: `/edit/SalesInvoice/${doc.name!}`,
query: { query: {
schemaName: 'SalesInvoice', schemaName: 'SalesInvoice',
values: { values: {
@ -186,7 +187,7 @@ export class Party extends Doc {
condition: (doc: Doc) => condition: (doc: Doc) =>
!doc.notInserted && (doc.role as PartyRole) !== 'Supplier', !doc.notInserted && (doc.role as PartyRole) !== 'Supplier',
action: async (partyDoc, router) => { action: async (partyDoc, router) => {
router.push({ await router.push({
path: '/list/SalesInvoice', path: '/list/SalesInvoice',
query: { filters: JSON.stringify({ party: partyDoc.name }) }, query: { filters: JSON.stringify({ party: partyDoc.name }) },
}); });

View File

@ -84,9 +84,7 @@ export class Payment extends Transactional {
updateAmountOnReferenceUpdate() { updateAmountOnReferenceUpdate() {
this.amount = this.fyo.pesa(0); this.amount = this.fyo.pesa(0);
for (const paymentReference of (this.for ?? []) as Doc[]) { for (const paymentReference of (this.for ?? []) as Doc[]) {
this.amount = (this.amount as Money).add( this.amount = this.amount.add(paymentReference.amount as Money);
paymentReference.amount as Money
);
} }
} }
@ -123,8 +121,9 @@ export class Payment extends Transactional {
if (referenceName && referenceType && !refDoc) { if (referenceName && referenceType && !refDoc) {
throw new ValidationError( throw new ValidationError(
t`${referenceType} of type ${this.fyo.schemaMap?.[referenceType] t`${referenceType} of type ${
?.label!} does not exist` this.fyo.schemaMap?.[referenceType]?.label ?? referenceType
} does not exist`
); );
} }
@ -241,8 +240,8 @@ export class Payment extends Transactional {
const account = this.account as string; const account = this.account as string;
const amount = this.amount as Money; const amount = this.amount as Money;
await posting.debit(paymentAccount as string, amount); await posting.debit(paymentAccount, amount);
await posting.credit(account as string, amount); await posting.credit(account, amount);
await this.applyWriteOffPosting(posting); await this.applyWriteOffPosting(posting);
return posting; return posting;
@ -269,7 +268,7 @@ export class Payment extends Transactional {
} }
async validateReferences() { async validateReferences() {
const forReferences = (this.for ?? []) as PaymentFor[]; const forReferences = this.for ?? [];
if (forReferences.length === 0) { if (forReferences.length === 0) {
return; return;
} }
@ -334,10 +333,10 @@ export class Payment extends Transactional {
} }
async updateReferenceDocOutstanding() { async updateReferenceDocOutstanding() {
for (const row of (this.for ?? []) as PaymentFor[]) { for (const row of this.for ?? []) {
const referenceDoc = await this.fyo.doc.getDoc( const referenceDoc = await this.fyo.doc.getDoc(
row.referenceType!, row.referenceType!,
row.referenceName! row.referenceName
); );
const previousOutstandingAmount = referenceDoc.outstandingAmount as Money; const previousOutstandingAmount = referenceDoc.outstandingAmount as Money;
@ -348,7 +347,7 @@ export class Payment extends Transactional {
async afterCancel() { async afterCancel() {
await super.afterCancel(); await super.afterCancel();
this.revertOutstandingAmount(); await this.revertOutstandingAmount();
} }
async revertOutstandingAmount() { async revertOutstandingAmount() {
@ -357,10 +356,10 @@ export class Payment extends Transactional {
} }
async _revertReferenceOutstanding() { async _revertReferenceOutstanding() {
for (const ref of (this.for ?? []) as PaymentFor[]) { for (const ref of this.for ?? []) {
const refDoc = await this.fyo.doc.getDoc( const refDoc = await this.fyo.doc.getDoc(
ref.referenceType!, ref.referenceType!,
ref.referenceName! ref.referenceName
); );
const outstandingAmount = (refDoc.outstandingAmount as Money).add( const outstandingAmount = (refDoc.outstandingAmount as Money).add(
@ -374,7 +373,7 @@ export class Payment extends Transactional {
async updatePartyOutstanding() { async updatePartyOutstanding() {
const partyDoc = (await this.fyo.doc.getDoc( const partyDoc = (await this.fyo.doc.getDoc(
ModelNameEnum.Party, ModelNameEnum.Party,
this.party! this.party
)) as Party; )) as Party;
await partyDoc.updateOutstandingAmount(); await partyDoc.updateOutstandingAmount();
} }
@ -439,7 +438,7 @@ export class Payment extends Transactional {
'referenceName' 'referenceName'
)) as Invoice | null; )) as Invoice | null;
return (refDoc?.account ?? null) as string | null; return refDoc?.account ?? null;
} }
formulas: FormulaMap = { formulas: FormulaMap = {
@ -514,17 +513,17 @@ export class Payment extends Transactional {
}, },
}, },
amount: { amount: {
formula: async () => this.getSum('for', 'amount', false), formula: () => this.getSum('for', 'amount', false),
dependsOn: ['for'], dependsOn: ['for'],
}, },
amountPaid: { amountPaid: {
formula: async () => this.amount!.sub(this.writeoff!), formula: () => this.amount!.sub(this.writeoff!),
dependsOn: ['amount', 'writeoff', 'for'], dependsOn: ['amount', 'writeoff', 'for'],
}, },
}; };
validations: ValidationMap = { validations: ValidationMap = {
amount: async (value: DocValue) => { amount: (value: DocValue) => {
if ((value as Money).isNegative()) { if ((value as Money).isNegative()) {
throw new ValidationError( throw new ValidationError(
this.fyo.t`Payment amount cannot be less than zero.` this.fyo.t`Payment amount cannot be less than zero.`
@ -615,7 +614,7 @@ export class Payment extends Transactional {
return [getLedgerLinkAction(fyo)]; return [getLedgerLinkAction(fyo)];
} }
static getListViewSettings(fyo: Fyo): ListViewSettings { static getListViewSettings(): ListViewSettings {
return { return {
columns: ['name', getDocStatusListColumn(), 'party', 'date', 'amount'], columns: ['name', getDocStatusListColumn(), 'party', 'date', 'amount'],
}; };

View File

@ -60,7 +60,7 @@ export class PaymentFor extends Doc {
const outstandingAmount = (await this.fyo.getValue( const outstandingAmount = (await this.fyo.getValue(
this.referenceType as string, this.referenceType as string,
this.referenceName as string, this.referenceName,
'outstandingAmount' 'outstandingAmount'
)) as Money; )) as Money;
@ -105,10 +105,11 @@ export class PaymentFor extends Doc {
return; return;
} }
const referenceType = this.referenceType ?? ModelNameEnum.SalesInvoice;
const label = this.fyo.schemaMap[referenceType]?.label ?? referenceType;
throw new NotFoundError( throw new NotFoundError(
t`${this.fyo.schemaMap[this.referenceType!]?.label!} ${ t`${label} ${value as string} does not exist`,
value as string
} does not exist`,
false false
); );
}, },

View File

@ -46,7 +46,7 @@ export class PrintTemplate extends Doc {
static lists: ListsMap = { static lists: ListsMap = {
type(doc?: Doc) { type(doc?: Doc) {
let enableInventory: boolean = false; let enableInventory = false;
let schemaMap: SchemaMap = {}; let schemaMap: SchemaMap = {};
if (doc) { if (doc) {
enableInventory = !!doc.fyo.singles.AccountingSettings?.enableInventory; enableInventory = !!doc.fyo.singles.AccountingSettings?.enableInventory;

View File

@ -24,7 +24,7 @@ export class PurchaseInvoice extends Invoice {
} }
} }
const discountAmount = await this.getTotalDiscount(); const discountAmount = this.getTotalDiscount();
const discountAccount = this.fyo.singles.AccountingSettings const discountAccount = this.fyo.singles.AccountingSettings
?.discountAccount as string | undefined; ?.discountAccount as string | undefined;
if (discountAccount && discountAmount.isPositive()) { if (discountAccount && discountAmount.isPositive()) {

View File

@ -19,12 +19,12 @@ export class SalesInvoice extends Invoice {
} }
if (this.taxes) { if (this.taxes) {
for (const tax of this.taxes!) { for (const tax of this.taxes) {
await posting.credit(tax.account!, tax.amount!.mul(exchangeRate)); await posting.credit(tax.account!, tax.amount!.mul(exchangeRate));
} }
} }
const discountAmount = await this.getTotalDiscount(); const discountAmount = this.getTotalDiscount();
const discountAccount = this.fyo.singles.AccountingSettings const discountAccount = this.fyo.singles.AccountingSettings
?.discountAccount as string | undefined; ?.discountAccount as string | undefined;
if (discountAccount && discountAmount.isPositive()) { if (discountAccount && discountAmount.isPositive()) {

View File

@ -61,7 +61,7 @@ export class SetupWizard extends Doc {
formulas: FormulaMap = { formulas: FormulaMap = {
fiscalYearStart: { fiscalYearStart: {
formula: async (fieldname?: string) => { formula: (fieldname?: string) => {
if ( if (
fieldname === 'fiscalYearEnd' && fieldname === 'fiscalYearEnd' &&
this.fiscalYearEnd && this.fiscalYearEnd &&
@ -85,7 +85,7 @@ export class SetupWizard extends Doc {
dependsOn: ['country', 'fiscalYearEnd'], dependsOn: ['country', 'fiscalYearEnd'],
}, },
fiscalYearEnd: { fiscalYearEnd: {
formula: async (fieldname?: string) => { formula: (fieldname?: string) => {
if ( if (
fieldname === 'fiscalYearStart' && fieldname === 'fiscalYearStart' &&
this.fiscalYearStart && this.fiscalYearStart &&
@ -109,7 +109,7 @@ export class SetupWizard extends Doc {
dependsOn: ['country', 'fiscalYearStart'], dependsOn: ['country', 'fiscalYearStart'],
}, },
currency: { currency: {
formula: async () => { formula: () => {
const country = this.get('country'); const country = this.get('country');
if (typeof country !== 'string') { if (typeof country !== 'string') {
return; return;
@ -135,7 +135,7 @@ export class SetupWizard extends Doc {
dependsOn: ['country'], dependsOn: ['country'],
}, },
chartOfAccounts: { chartOfAccounts: {
formula: async () => { formula: () => {
const country = this.get('country') as string | undefined; const country = this.get('country') as string | undefined;
if (country === undefined) { if (country === undefined) {
return; return;

View File

@ -9,10 +9,7 @@ import {
AccountRootType, AccountRootType,
AccountRootTypeEnum, AccountRootTypeEnum,
} from './baseModels/Account/types'; } from './baseModels/Account/types';
import { import { numberSeriesDefaultsMap } from './baseModels/Defaults/Defaults';
Defaults,
numberSeriesDefaultsMap,
} from './baseModels/Defaults/Defaults';
import { Invoice } from './baseModels/Invoice/Invoice'; import { Invoice } from './baseModels/Invoice/Invoice';
import { StockMovement } from './inventory/StockMovement'; import { StockMovement } from './inventory/StockMovement';
import { StockTransfer } from './inventory/StockTransfer'; import { StockTransfer } from './inventory/StockTransfer';
@ -55,7 +52,7 @@ export function getMakeStockTransferAction(
condition: (doc: Doc) => doc.isSubmitted && !!doc.stockNotTransferred, condition: (doc: Doc) => doc.isSubmitted && !!doc.stockNotTransferred,
action: async (doc: Doc) => { action: async (doc: Doc) => {
const transfer = await (doc as Invoice).getStockTransfer(); const transfer = await (doc as Invoice).getStockTransfer();
if (!transfer) { if (!transfer || !transfer.name) {
return; return;
} }
@ -81,7 +78,7 @@ export function getMakeInvoiceAction(
condition: (doc: Doc) => doc.isSubmitted && !doc.backReference, condition: (doc: Doc) => doc.isSubmitted && !doc.backReference,
action: async (doc: Doc) => { action: async (doc: Doc) => {
const invoice = await (doc as StockTransfer).getInvoice(); const invoice = await (doc as StockTransfer).getInvoice();
if (!invoice) { if (!invoice || !invoice.name) {
return; return;
} }
@ -128,10 +125,7 @@ export function getMakePaymentAction(fyo: Fyo): Action {
}; };
} }
export function getLedgerLinkAction( export function getLedgerLinkAction(fyo: Fyo, isStock = false): Action {
fyo: Fyo,
isStock: boolean = false
): Action {
let label = fyo.t`Accounting Entries`; let label = fyo.t`Accounting Entries`;
let reportClassName: 'GeneralLedger' | 'StockLedger' = 'GeneralLedger'; let reportClassName: 'GeneralLedger' | 'StockLedger' = 'GeneralLedger';
@ -146,7 +140,7 @@ export function getLedgerLinkAction(
condition: (doc: Doc) => doc.isSubmitted, condition: (doc: Doc) => doc.isSubmitted,
action: async (doc: Doc, router: Router) => { action: async (doc: Doc, router: Router) => {
const route = getLedgerLink(doc, reportClassName); const route = getLedgerLink(doc, reportClassName);
router.push(route); await router.push(route);
}, },
}; };
} }
@ -174,7 +168,7 @@ export function getTransactionStatusColumn(): ColumnConfig {
fieldtype: 'Select', fieldtype: 'Select',
render(doc) { render(doc) {
const status = getDocStatus(doc) as InvoiceStatus; const status = getDocStatus(doc) as InvoiceStatus;
const color = statusColor[status]; const color = statusColor[status] ?? 'gray';
const label = getStatusText(status); const label = getStatusText(status);
return { return {
@ -389,9 +383,7 @@ export async function getExchangeRate({
let exchangeRate = 0; let exchangeRate = 0;
if (localStorage) { if (localStorage) {
exchangeRate = safeParseFloat( exchangeRate = safeParseFloat(localStorage.getItem(cacheKey) as string);
localStorage.getItem(cacheKey as string) as string
);
} }
if (exchangeRate && exchangeRate !== 1) { if (exchangeRate && exchangeRate !== 1) {
@ -402,9 +394,14 @@ export async function getExchangeRate({
const res = await fetch( const res = await fetch(
`https://api.vatcomply.com/rates?date=${date}&base=${fromCurrency}&symbols=${toCurrency}` `https://api.vatcomply.com/rates?date=${date}&base=${fromCurrency}&symbols=${toCurrency}`
); );
const data = await res.json(); const data = (await res.json()) as {
base: string;
data: string;
rates: Record<string, number>;
};
exchangeRate = data.rates[toCurrency]; exchangeRate = data.rates[toCurrency];
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console
console.error(error); console.error(error);
exchangeRate ??= 1; exchangeRate ??= 1;
} }
@ -439,7 +436,7 @@ export function getNumberSeries(schemaName: string, fyo: Fyo) {
return undefined; return undefined;
} }
const defaults = fyo.singles.Defaults as Defaults | undefined; const defaults = fyo.singles.Defaults;
const field = fyo.getField(schemaName, 'numberSeries'); const field = fyo.getField(schemaName, 'numberSeries');
const value = defaults?.[numberSeriesKey] as string | undefined; const value = defaults?.[numberSeriesKey] as string | undefined;
return value ?? (field?.default as string | undefined); return value ?? (field?.default as string | undefined);

View File

@ -40,7 +40,7 @@ export class StockManager {
} }
for (const details of detailsList) { for (const details of detailsList) {
await this.#createTransfer(details); this.#createTransfer(details);
} }
await this.#sync(); await this.#sync();
@ -73,7 +73,7 @@ export class StockManager {
} }
} }
async #createTransfer(details: SMIDetails) { #createTransfer(details: SMIDetails) {
const item = new StockManagerItem(details, this.fyo); const item = new StockManagerItem(details, this.fyo);
item.transferStock(); item.transferStock();
this.items.push(item); this.items.push(item);

View File

@ -23,7 +23,7 @@ import {
getSerialNumberFromDoc, getSerialNumberFromDoc,
updateSerialNumbers, updateSerialNumbers,
validateBatch, validateBatch,
validateSerialNumber validateSerialNumber,
} from './helpers'; } from './helpers';
import { MovementType, MovementTypeEnum } from './types'; import { MovementType, MovementTypeEnum } from './types';
@ -39,6 +39,7 @@ export class StockMovement extends Transfer {
return false; return false;
} }
// eslint-disable-next-line @typescript-eslint/require-await
override async getPosting(): Promise<LedgerPosting | null> { override async getPosting(): Promise<LedgerPosting | null> {
return null; return null;
} }

View File

@ -1,6 +1,5 @@
import { t } from 'fyo'; import { t } from 'fyo';
import { DocValue } from 'fyo/core/types'; import { DocValue } from 'fyo/core/types';
import { Doc } from 'fyo/model/doc';
import { import {
FiltersMap, FiltersMap,
FormulaMap, FormulaMap,
@ -14,8 +13,8 @@ import { ModelNameEnum } from 'models/types';
import { Money } from 'pesa'; import { Money } from 'pesa';
import { safeParseFloat } from 'utils/index'; import { safeParseFloat } from 'utils/index';
import { StockMovement } from './StockMovement'; import { StockMovement } from './StockMovement';
import { MovementTypeEnum } from './types';
import { TransferItem } from './TransferItem'; import { TransferItem } from './TransferItem';
import { MovementTypeEnum } from './types';
export class StockMovementItem extends TransferItem { export class StockMovementItem extends TransferItem {
name?: string; name?: string;
@ -78,8 +77,8 @@ export class StockMovementItem extends TransferItem {
return null; return null;
} }
const defaultLocation = this.fyo.singles.InventorySettings const defaultLocation =
?.defaultLocation as string | undefined; this.fyo.singles.InventorySettings?.defaultLocation;
if (defaultLocation && !this.fromLocation && this.isIssue) { if (defaultLocation && !this.fromLocation && this.isIssue) {
return defaultLocation; return defaultLocation;
} }
@ -94,8 +93,8 @@ export class StockMovementItem extends TransferItem {
return null; return null;
} }
const defaultLocation = this.fyo.singles.InventorySettings const defaultLocation =
?.defaultLocation as string | undefined; this.fyo.singles.InventorySettings?.defaultLocation;
if (defaultLocation && !this.toLocation && this.isReceipt) { if (defaultLocation && !this.toLocation && this.isReceipt) {
return defaultLocation; return defaultLocation;
} }
@ -128,7 +127,7 @@ export class StockMovementItem extends TransferItem {
dependsOn: ['item', 'unit'], dependsOn: ['item', 'unit'],
}, },
transferQuantity: { transferQuantity: {
formula: async (fieldname) => { formula: (fieldname) => {
if (fieldname === 'quantity' || this.unit === this.transferUnit) { if (fieldname === 'quantity' || this.unit === this.transferUnit) {
return this.quantity; return this.quantity;
} }
@ -145,7 +144,7 @@ export class StockMovementItem extends TransferItem {
const itemDoc = await this.fyo.doc.getDoc( const itemDoc = await this.fyo.doc.getDoc(
ModelNameEnum.Item, ModelNameEnum.Item,
this.item as string this.item
); );
const unitDoc = itemDoc.getLink('uom'); const unitDoc = itemDoc.getLink('uom');
@ -217,13 +216,14 @@ export class StockMovementItem extends TransferItem {
const item = await this.fyo.db.getAll(ModelNameEnum.UOMConversionItem, { const item = await this.fyo.db.getAll(ModelNameEnum.UOMConversionItem, {
fields: ['parent'], fields: ['parent'],
filters: { uom: value as string, parent: this.item! }, filters: { uom: value as string, parent: this.item },
}); });
if (item.length < 1) if (item.length < 1)
throw new ValidationError( throw new ValidationError(
t`Transfer Unit ${value as string} is not applicable for Item ${this t`Transfer Unit ${value as string} is not applicable for Item ${
.item!}` this.item
}`
); );
}, },
}; };

View File

@ -26,7 +26,6 @@ import {
validateBatch, validateBatch,
validateSerialNumber, validateSerialNumber,
} from './helpers'; } from './helpers';
import { Item } from 'models/baseModels/Item/Item';
export abstract class StockTransfer extends Transfer { export abstract class StockTransfer extends Transfer {
name?: string; name?: string;
@ -67,7 +66,7 @@ export abstract class StockTransfer extends Transfer {
static defaults: DefaultMap = { static defaults: DefaultMap = {
numberSeries: (doc) => getNumberSeries(doc.schemaName, doc.fyo), numberSeries: (doc) => getNumberSeries(doc.schemaName, doc.fyo),
terms: (doc) => { terms: (doc) => {
const defaults = doc.fyo.singles.Defaults as Defaults | undefined; const defaults = doc.fyo.singles.Defaults;
if (doc.schemaName === ModelNameEnum.Shipment) { if (doc.schemaName === ModelNameEnum.Shipment) {
return defaults?.shipmentTerms ?? ''; return defaults?.shipmentTerms ?? '';
} }

View File

@ -1,4 +1,3 @@
import { t } from 'fyo';
import { DocValue } from 'fyo/core/types'; import { DocValue } from 'fyo/core/types';
import { Doc } from 'fyo/model/doc'; import { Doc } from 'fyo/model/doc';
import { import {
@ -73,7 +72,7 @@ export class StockTransferItem extends TransferItem {
dependsOn: ['item', 'unit'], dependsOn: ['item', 'unit'],
}, },
transferQuantity: { transferQuantity: {
formula: async (fieldname) => { formula: (fieldname) => {
if (fieldname === 'quantity' || this.unit === this.transferUnit) { if (fieldname === 'quantity' || this.unit === this.transferUnit) {
return this.quantity; return this.quantity;
} }
@ -90,7 +89,7 @@ export class StockTransferItem extends TransferItem {
const itemDoc = await this.fyo.doc.getDoc( const itemDoc = await this.fyo.doc.getDoc(
ModelNameEnum.Item, ModelNameEnum.Item,
this.item as string this.item
); );
const unitDoc = itemDoc.getLink('uom'); const unitDoc = itemDoc.getLink('uom');
@ -146,7 +145,7 @@ export class StockTransferItem extends TransferItem {
dependsOn: ['rate', 'quantity'], dependsOn: ['rate', 'quantity'],
}, },
rate: { rate: {
formula: async (fieldname) => { formula: async () => {
const rate = (await this.fyo.getValue( const rate = (await this.fyo.getValue(
'Item', 'Item',
this.item as string, this.item as string,
@ -177,8 +176,8 @@ export class StockTransferItem extends TransferItem {
return; return;
} }
const defaultLocation = this.fyo.singles.InventorySettings const defaultLocation =
?.defaultLocation as string | undefined; this.fyo.singles.InventorySettings?.defaultLocation;
if (defaultLocation && !this.location) { if (defaultLocation && !this.location) {
return defaultLocation; return defaultLocation;
@ -195,13 +194,14 @@ export class StockTransferItem extends TransferItem {
const item = await this.fyo.db.getAll(ModelNameEnum.UOMConversionItem, { const item = await this.fyo.db.getAll(ModelNameEnum.UOMConversionItem, {
fields: ['parent'], fields: ['parent'],
filters: { uom: value as string, parent: this.item! }, filters: { uom: value as string, parent: this.item },
}); });
if (item.length < 1) if (item.length < 1)
throw new ValidationError( throw new ValidationError(
t`Transfer Unit ${value as string} is not applicable for Item ${this this.fyo.t`Transfer Unit ${
.item!}` value as string
} is not applicable for Item ${this.item}`
); );
}, },
}; };

View File

@ -55,7 +55,7 @@ export class StockQueue {
return null; return null;
} }
let incomingRate: number = 0; let incomingRate = 0;
this.quantity -= quantity; this.quantity -= quantity;
let remaining = quantity; let remaining = quantity;

View File

@ -37,17 +37,22 @@ interface TransferTwo extends Omit<Transfer, 'from' | 'to'> {
location: string; location: string;
} }
export function getItem(name: string, rate: number, hasBatch: boolean = false, hasSerialNumber: boolean = false) { export function getItem(
name: string,
rate: number,
hasBatch = false,
hasSerialNumber = false
) {
return { name, rate, trackItem: true, hasBatch, hasSerialNumber }; return { name, rate, trackItem: true, hasBatch, hasSerialNumber };
} }
export async function getBatch( export function getBatch(
schemaName: ModelNameEnum.Batch, schemaName: ModelNameEnum.Batch,
batch: string, batch: string,
expiryDate: Date, expiryDate: Date,
manufactureDate: Date, manufactureDate: Date,
fyo: Fyo fyo: Fyo
): Promise<Batch> { ): Batch {
const doc = fyo.doc.getNewDoc(schemaName, { const doc = fyo.doc.getNewDoc(schemaName, {
batch, batch,
expiryDate, expiryDate,

View File

@ -5,7 +5,7 @@ import { codeStateMap } from 'regional/in';
export class Address extends BaseAddress { export class Address extends BaseAddress {
formulas: FormulaMap = { formulas: FormulaMap = {
addressDisplay: { addressDisplay: {
formula: async () => { formula: () => {
return [ return [
this.addressLine1, this.addressLine1,
this.addressLine2, this.addressLine2,
@ -28,7 +28,7 @@ export class Address extends BaseAddress {
}, },
pos: { pos: {
formula: async () => { formula: () => {
const stateList = Object.values(codeStateMap).sort(); const stateList = Object.values(codeStateMap).sort();
const state = this.state as string; const state = this.state as string;
if (stateList.includes(state)) { if (stateList.includes(state)) {

View File

@ -6,6 +6,7 @@ export class Party extends BaseParty {
gstin?: string; gstin?: string;
gstType?: GSTType; gstType?: GSTType;
// eslint-disable-next-line @typescript-eslint/require-await
async beforeSync() { async beforeSync() {
const gstin = this.get('gstin') as string | undefined; const gstin = this.get('gstin') as string | undefined;
const gstType = this.get('gstType') as GSTType; const gstType = this.get('gstType') as GSTType;

View File

@ -34,11 +34,11 @@ export const ACC_BAL_WIDTH = 1.25;
export abstract class AccountReport extends LedgerReport { export abstract class AccountReport extends LedgerReport {
toDate?: string; toDate?: string;
count: number = 3; count = 3;
fromYear?: number; fromYear?: number;
toYear?: number; toYear?: number;
consolidateColumns: boolean = false; consolidateColumns = false;
hideGroupAmounts: boolean = false; hideGroupAmounts = false;
periodicity: Periodicity = 'Monthly'; periodicity: Periodicity = 'Monthly';
basedOn: BasedOn = 'Until Date'; basedOn: BasedOn = 'Until Date';
@ -88,10 +88,7 @@ export abstract class AccountReport extends LedgerReport {
}; };
} }
async getTotalNode( getTotalNode(rootNode: AccountTreeNode, name: string): AccountListNode {
rootNode: AccountTreeNode,
name: string
): Promise<AccountListNode> {
const accountTree = { [rootNode.name]: rootNode }; const accountTree = { [rootNode.name]: rootNode };
const leafNodes = getListOfLeafNodes(accountTree) as AccountTreeNode[]; const leafNodes = getListOfLeafNodes(accountTree) as AccountTreeNode[];
@ -153,7 +150,7 @@ export abstract class AccountReport extends LedgerReport {
): Promise<AccountNameValueMapMap> { ): Promise<AccountNameValueMapMap> {
const accountValueMap: AccountNameValueMapMap = new Map(); const accountValueMap: AccountNameValueMapMap = new Map();
if (!this.accountMap) { if (!this.accountMap) {
await this._getAccountMap(); await this._setAndReturnAccountMap();
} }
for (const account of map.keys()) { for (const account of map.keys()) {
@ -169,17 +166,17 @@ export abstract class AccountReport extends LedgerReport {
} }
if (!this.accountMap?.[entry.account]) { if (!this.accountMap?.[entry.account]) {
this._getAccountMap(true); await this._setAndReturnAccountMap(true);
} }
const totalBalance = valueMap.get(key!)?.balance ?? 0; const totalBalance = valueMap.get(key)?.balance ?? 0;
const balance = (entry.debit ?? 0) - (entry.credit ?? 0); const balance = (entry.debit ?? 0) - (entry.credit ?? 0);
const rootType = this.accountMap![entry.account]?.rootType; const rootType = this.accountMap![entry.account]?.rootType;
if (isCredit(rootType)) { if (isCredit(rootType)) {
valueMap.set(key!, { balance: totalBalance - balance }); valueMap.set(key, { balance: totalBalance - balance });
} else { } else {
valueMap.set(key!, { balance: totalBalance + balance }); valueMap.set(key, { balance: totalBalance + balance });
} }
} }
accountValueMap.set(account, valueMap); accountValueMap.set(account, valueMap);
@ -189,7 +186,9 @@ export abstract class AccountReport extends LedgerReport {
} }
async _getAccountTree(rangeGroupedMap: AccountNameValueMapMap) { async _getAccountTree(rangeGroupedMap: AccountNameValueMapMap) {
const accountTree = cloneDeep(await this._getAccountMap()) as AccountTree; const accountTree = cloneDeep(
await this._setAndReturnAccountMap()
) as AccountTree;
setPruneFlagOnAccountTreeNodes(accountTree); setPruneFlagOnAccountTreeNodes(accountTree);
setValueMapOnAccountTreeNodes(accountTree, rangeGroupedMap); setValueMapOnAccountTreeNodes(accountTree, rangeGroupedMap);
@ -200,7 +199,7 @@ export abstract class AccountReport extends LedgerReport {
return accountTree; return accountTree;
} }
async _getAccountMap(force: boolean = false) { async _setAndReturnAccountMap(force = false) {
if (this.accountMap && !force) { if (this.accountMap && !force) {
return this.accountMap; return this.accountMap;
} }
@ -510,14 +509,14 @@ function updateParentAccountWithChildValues(
parentAccount.valueMap ??= new Map(); parentAccount.valueMap ??= new Map();
for (const key of valueMap.keys()) { for (const key of valueMap.keys()) {
const value = parentAccount.valueMap!.get(key); const value = parentAccount.valueMap.get(key);
const childValue = valueMap.get(key); const childValue = valueMap.get(key);
const map: Record<string, number> = {}; const map: Record<string, number> = {};
for (const key of Object.keys(childValue!)) { for (const key of Object.keys(childValue!)) {
map[key] = (value?.[key] ?? 0) + (childValue?.[key] ?? 0); map[key] = (value?.[key] ?? 0) + (childValue?.[key] ?? 0);
} }
parentAccount.valueMap!.set(key, map); parentAccount.valueMap.set(key, map);
} }
return parentAccount.parentAccount!; return parentAccount.parentAccount!;
@ -533,7 +532,7 @@ function setChildrenOnAccountTreeNodes(accountTree: AccountTree) {
} }
accountTree[ac.parentAccount].children ??= []; accountTree[ac.parentAccount].children ??= [];
accountTree[ac.parentAccount].children!.push(ac!); accountTree[ac.parentAccount].children!.push(ac);
parentNodes.add(ac.parentAccount); parentNodes.add(ac.parentAccount);
} }

View File

@ -13,7 +13,7 @@ import { getMapFromList } from 'utils';
export class BalanceSheet extends AccountReport { export class BalanceSheet extends AccountReport {
static title = t`Balance Sheet`; static title = t`Balance Sheet`;
static reportName = 'balance-sheet'; static reportName = 'balance-sheet';
loading: boolean = false; loading = false;
get rootTypes(): AccountRootType[] { get rootTypes(): AccountRootType[] {
return [ return [
@ -54,15 +54,15 @@ export class BalanceSheet extends AccountReport {
}) })
.filter((row) => !!row.rootNode); .filter((row) => !!row.rootNode);
this.reportData = await this.getReportDataFromRows( this.reportData = this.getReportDataFromRows(
getMapFromList(rootTypeRows, 'rootType') getMapFromList(rootTypeRows, 'rootType')
); );
this.loading = false; this.loading = false;
} }
async getReportDataFromRows( getReportDataFromRows(
rootTypeRows: Record<AccountRootType, RootTypeRow | undefined> rootTypeRows: Record<AccountRootType, RootTypeRow | undefined>
): Promise<ReportData> { ): ReportData {
const typeNameList = [ const typeNameList = [
{ {
rootType: AccountRootTypeEnum.Asset, rootType: AccountRootTypeEnum.Asset,
@ -89,7 +89,7 @@ export class BalanceSheet extends AccountReport {
reportData.push(...row.rows); reportData.push(...row.rows);
if (row.rootNode) { if (row.rootNode) {
const totalNode = await this.getTotalNode(row.rootNode, totalName); const totalNode = this.getTotalNode(row.rootNode, totalName);
const totalRow = this.getRowFromAccountListNode(totalNode); const totalRow = this.getRowFromAccountListNode(totalNode);
reportData.push(totalRow); reportData.push(totalRow);
} }

View File

@ -24,11 +24,11 @@ type ReferenceType =
export class GeneralLedger extends LedgerReport { export class GeneralLedger extends LedgerReport {
static title = t`General Ledger`; static title = t`General Ledger`;
static reportName = 'general-ledger'; static reportName = 'general-ledger';
usePagination: boolean = true; usePagination = true;
loading: boolean = false; loading = false;
ascending: boolean = false; ascending = false;
reverted: boolean = false; reverted = false;
referenceType: ReferenceType = 'All'; referenceType: ReferenceType = 'All';
groupBy: 'none' | 'party' | 'account' | 'referenceName' = 'none'; groupBy: 'none' | 'party' | 'account' | 'referenceName' = 'none';
_rawData: LedgerEntry[] = []; _rawData: LedgerEntry[] = [];
@ -37,7 +37,7 @@ export class GeneralLedger extends LedgerReport {
super(fyo); super(fyo);
} }
async setDefaultFilters() { setDefaultFilters() {
if (!this.toDate) { if (!this.toDate) {
this.toDate = DateTime.now().plus({ days: 1 }).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();
@ -239,7 +239,7 @@ export class GeneralLedger extends LedgerReport {
return { totalDebit, totalCredit }; return { totalDebit, totalCredit };
} }
async _getQueryFilters(): Promise<QueryFilter> { _getQueryFilters(): QueryFilter {
const filters: QueryFilter = {}; const filters: QueryFilter = {};
const stringFilters = ['account', 'party', 'referenceName']; const stringFilters = ['account', 'party', 'referenceName'];

View File

@ -17,9 +17,9 @@ export abstract class BaseGSTR extends Report {
toDate?: string; toDate?: string;
fromDate?: string; fromDate?: string;
transferType?: TransferType; transferType?: TransferType;
usePagination: boolean = true; usePagination = true;
gstrRows?: GSTRRow[]; gstrRows?: GSTRRow[];
loading: boolean = false; loading = false;
abstract gstrType: GSTRType; abstract gstrType: GSTRType;
@ -111,7 +111,7 @@ export abstract class BaseGSTR extends Report {
return (row) => row.rate === 0; // this takes care of both nil rated, exempted goods return (row) => row.rate === 0; // this takes care of both nil rated, exempted goods
} }
return (_) => true; return () => true;
} }
async getEntries() { async getEntries() {
@ -133,7 +133,7 @@ export abstract class BaseGSTR extends Report {
const entries = await this.getEntries(); const entries = await this.getEntries();
const gstrRows: GSTRRow[] = []; const gstrRows: GSTRRow[] = [];
for (const entry of entries) { for (const entry of entries) {
const gstrRow = await this.getGstrRow(entry.name as string); const gstrRow = await this.getGstrRow(entry.name);
gstrRows.push(gstrRow); gstrRows.push(gstrRow);
} }
return gstrRows; return gstrRows;
@ -149,7 +149,7 @@ export abstract class BaseGSTR extends Report {
'gstin' 'gstin'
)) as string | null; )) as string | null;
const party = (await this.fyo.doc.getDoc('Party', entry.party!)) as Party; const party = (await this.fyo.doc.getDoc('Party', entry.party)) as Party;
let place = ''; let place = '';
if (party.address) { if (party.address) {
@ -216,7 +216,7 @@ export abstract class BaseGSTR extends Report {
} }
} }
async setDefaultFilters() { setDefaultFilters() {
if (!this.toDate) { if (!this.toDate) {
this.toDate = DateTime.local().toISODate(); this.toDate = DateTime.local().toISODate();
} }

View File

@ -175,7 +175,7 @@ async function getCanExport(report: BaseGSTR) {
return true; return true;
} }
showDialog({ await showDialog({
title: report.fyo.t`Cannot Export`, title: report.fyo.t`Cannot Export`,
detail: report.fyo.t`Please set GSTIN in General Settings.`, detail: report.fyo.t`Please set GSTIN in General Settings.`,
type: 'error', type: 'error',
@ -204,7 +204,7 @@ export async function getGstrJsonData(report: BaseGSTR): Promise<string> {
} else if (transferType === TransferTypeEnum.B2CL) { } else if (transferType === TransferTypeEnum.B2CL) {
gstData.b2cl = await generateB2clData(report); gstData.b2cl = await generateB2clData(report);
} else if (transferType === TransferTypeEnum.B2CS) { } else if (transferType === TransferTypeEnum.B2CS) {
gstData.b2cs = await generateB2csData(report); gstData.b2cs = generateB2csData(report);
} }
return JSON.stringify(gstData); return JSON.stringify(gstData);

View File

@ -14,7 +14,7 @@ export abstract class LedgerReport extends Report {
static reportName = 'general-ledger'; static reportName = 'general-ledger';
_rawData: LedgerEntry[] = []; _rawData: LedgerEntry[] = [];
shouldRefresh: boolean = false; shouldRefresh = false;
constructor(fyo: Fyo) { constructor(fyo: Fyo) {
super(fyo); super(fyo);
@ -117,7 +117,7 @@ export abstract class LedgerReport extends Report {
}); });
} }
abstract _getQueryFilters(): Promise<QueryFilter>; abstract _getQueryFilters(): QueryFilter | Promise<QueryFilter>;
getActions(): Action[] { getActions(): Action[] {
return getCommonExportActions(this); return getCommonExportActions(this);

View File

@ -17,7 +17,7 @@ import {
export class ProfitAndLoss extends AccountReport { export class ProfitAndLoss extends AccountReport {
static title = t`Profit And Loss`; static title = t`Profit And Loss`;
static reportName = 'profit-and-loss'; static reportName = 'profit-and-loss';
loading: boolean = false; loading = false;
get rootTypes(): AccountRootType[] { get rootTypes(): AccountRootType[] {
return [AccountRootTypeEnum.Income, AccountRootTypeEnum.Expense]; return [AccountRootTypeEnum.Income, AccountRootTypeEnum.Expense];
@ -62,7 +62,7 @@ export class ProfitAndLoss extends AccountReport {
const expenseList = convertAccountRootNodeToAccountList(expenseRoot); const expenseList = convertAccountRootNodeToAccountList(expenseRoot);
const expenseRows = this.getReportRowsFromAccountList(expenseList); const expenseRows = this.getReportRowsFromAccountList(expenseList);
this.reportData = await this.getReportDataFromRows( this.reportData = this.getReportDataFromRows(
incomeRows, incomeRows,
expenseRows, expenseRows,
incomeRoot, incomeRoot,
@ -71,14 +71,14 @@ export class ProfitAndLoss extends AccountReport {
this.loading = false; this.loading = false;
} }
async getReportDataFromRows( getReportDataFromRows(
incomeRows: ReportData, incomeRows: ReportData,
expenseRows: ReportData, expenseRows: ReportData,
incomeRoot: AccountTreeNode | undefined, incomeRoot: AccountTreeNode | undefined,
expenseRoot: AccountTreeNode | undefined expenseRoot: AccountTreeNode | undefined
): Promise<ReportData> { ): ReportData {
if (incomeRoot && !expenseRoot) { if (incomeRoot && !expenseRoot) {
return await this.getIncomeOrExpenseRows( return this.getIncomeOrExpenseRows(
incomeRoot, incomeRoot,
incomeRows, incomeRows,
t`Total Income (Credit)` t`Total Income (Credit)`
@ -86,7 +86,7 @@ export class ProfitAndLoss extends AccountReport {
} }
if (expenseRoot && !incomeRoot) { if (expenseRoot && !incomeRoot) {
return await this.getIncomeOrExpenseRows( return this.getIncomeOrExpenseRows(
expenseRoot, expenseRoot,
expenseRows, expenseRows,
t`Total Income (Credit)` t`Total Income (Credit)`
@ -97,7 +97,7 @@ export class ProfitAndLoss extends AccountReport {
return []; return [];
} }
return await this.getIncomeAndExpenseRows( return this.getIncomeAndExpenseRows(
incomeRows, incomeRows,
expenseRows, expenseRows,
incomeRoot, incomeRoot,
@ -105,30 +105,27 @@ export class ProfitAndLoss extends AccountReport {
); );
} }
async getIncomeOrExpenseRows( getIncomeOrExpenseRows(
root: AccountTreeNode, root: AccountTreeNode,
rows: ReportData, rows: ReportData,
totalRowName: string totalRowName: string
): Promise<ReportData> { ): ReportData {
const total = await this.getTotalNode(root, totalRowName); const total = this.getTotalNode(root, totalRowName);
const totalRow = this.getRowFromAccountListNode(total); const totalRow = this.getRowFromAccountListNode(total);
return [rows, totalRow].flat(); return [rows, totalRow].flat();
} }
async getIncomeAndExpenseRows( getIncomeAndExpenseRows(
incomeRows: ReportData, incomeRows: ReportData,
expenseRows: ReportData, expenseRows: ReportData,
incomeRoot: AccountTreeNode, incomeRoot: AccountTreeNode,
expenseRoot: AccountTreeNode expenseRoot: AccountTreeNode
) { ) {
const totalIncome = await this.getTotalNode( const totalIncome = this.getTotalNode(incomeRoot, t`Total Income (Credit)`);
incomeRoot,
t`Total Income (Credit)`
);
const totalIncomeRow = this.getRowFromAccountListNode(totalIncome); const totalIncomeRow = this.getRowFromAccountListNode(totalIncome);
const totalExpense = await this.getTotalNode( const totalExpense = this.getTotalNode(
expenseRoot, expenseRoot,
t`Total Expense (Debit)` t`Total Expense (Debit)`
); );

View File

@ -10,14 +10,14 @@ import { ColumnField, ReportData } from './types';
export abstract class Report extends Observable<RawValue> { export abstract class Report extends Observable<RawValue> {
static title: string; static title: string;
static reportName: string; static reportName: string;
static isInventory: boolean = false; static isInventory = false;
fyo: Fyo; fyo: Fyo;
columns: ColumnField[] = []; columns: ColumnField[] = [];
filters: Field[] = []; filters: Field[] = [];
reportData: ReportData; reportData: ReportData;
usePagination: boolean = false; usePagination = false;
shouldRefresh: boolean = false; shouldRefresh = false;
abstract loading: boolean; abstract loading: boolean;
constructor(fyo: Fyo) { constructor(fyo: Fyo) {
@ -26,14 +26,12 @@ export abstract class Report extends Observable<RawValue> {
this.reportData = []; this.reportData = [];
} }
get title() { get title(): string {
// @ts-ignore return (this.constructor as typeof Report).title;
return this.constructor.title;
} }
get reportName() { get reportName(): string {
// @ts-ignore return (this.constructor as typeof Report).reportName;
return this.constructor.reportName;
} }
async initialize() { async initialize() {
@ -61,7 +59,7 @@ export abstract class Report extends Observable<RawValue> {
return filterMap; return filterMap;
} }
async set(key: string, value: DocValue, callPostSet: boolean = true) { async set(key: string, value: DocValue, callPostSet = true) {
const field = this.filters.find((f) => f.fieldname === key); const field = this.filters.find((f) => f.fieldname === key);
if (field === undefined) { if (field === undefined) {
return; return;
@ -95,7 +93,7 @@ export abstract class Report extends Observable<RawValue> {
* Should first check if filter value is set * Should first check if filter value is set
* and update only if it is not set. * and update only if it is not set.
*/ */
async setDefaultFilters() {} abstract setDefaultFilters(): void | Promise<void>;
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[]>;

View File

@ -35,8 +35,8 @@ export class TrialBalance extends AccountReport {
fromDate?: string; fromDate?: string;
toDate?: string; toDate?: string;
hideGroupAmounts: boolean = false; hideGroupAmounts = false;
loading: boolean = false; loading = false;
_rawData: LedgerEntry[] = []; _rawData: LedgerEntry[] = [];
_dateRanges?: DateRange[]; _dateRanges?: DateRange[];
@ -79,6 +79,7 @@ export class TrialBalance extends AccountReport {
this.loading = false; this.loading = false;
} }
// eslint-disable-next-line @typescript-eslint/require-await
async getReportDataFromRows( async getReportDataFromRows(
rootTypeRows: RootTypeRow[] rootTypeRows: RootTypeRow[]
): Promise<ReportData> { ): Promise<ReportData> {
@ -93,6 +94,7 @@ export class TrialBalance extends AccountReport {
return reportData; return reportData;
} }
// eslint-disable-next-line @typescript-eslint/require-await
async _getGroupedByDateRanges( async _getGroupedByDateRanges(
map: GroupedMap map: GroupedMap
): Promise<AccountNameValueMapMap> { ): Promise<AccountNameValueMapMap> {
@ -108,11 +110,11 @@ export class TrialBalance extends AccountReport {
const key = this._getRangeMapKey(entry); const key = this._getRangeMapKey(entry);
if (key === null) { if (key === null) {
throw new ValueError( throw new ValueError(
`invalid entry in trial balance ${entry.date?.toISOString()}` `invalid entry in trial balance ${entry.date?.toISOString() ?? ''}`
); );
} }
const map = valueMap.get(key!); const map = valueMap.get(key);
const totalCredit = map?.credit ?? 0; const totalCredit = map?.credit ?? 0;
const totalDebit = map?.debit ?? 0; const totalDebit = map?.debit ?? 0;
@ -188,6 +190,7 @@ export class TrialBalance extends AccountReport {
} as ReportRow; } as ReportRow;
} }
// eslint-disable-next-line @typescript-eslint/require-await
async _getQueryFilters(): Promise<QueryFilter> { async _getQueryFilters(): Promise<QueryFilter> {
const filters: QueryFilter = {}; const filters: QueryFilter = {};
filters.reverted = false; filters.reverted = false;

View File

@ -1,4 +1,4 @@
import { Fyo, t } from 'fyo'; import { t } from 'fyo';
import { Action } from 'fyo/model/types'; import { Action } from 'fyo/model/types';
import { Verb } from 'fyo/telemetry/types'; import { Verb } from 'fyo/telemetry/types';
import { getSavePath, saveData, showExportInFolder } from 'src/utils/ipcCalls'; import { getSavePath, saveData, showExportInFolder } from 'src/utils/ipcCalls';
@ -88,7 +88,7 @@ function getJsonData(report: Report): string {
} }
const rowObj: Record<string, unknown> = {}; const rowObj: Record<string, unknown> = {};
for (const c in row.cells) { for (let c = 0; c < row.cells.length; c++) {
const { label } = columns[c]; const { label } = columns[c];
const cell = getValueFromCell(row.cells[c], displayPrecision); const cell = getValueFromCell(row.cells[c], displayPrecision);
rowObj[label] = cell; rowObj[label] = cell;
@ -129,7 +129,7 @@ function convertReportToCSVMatrix(report: Report): unknown[][] {
const displayPrecision = const displayPrecision =
(report.fyo.singles.SystemSettings?.displayPrecision as number) ?? 2; (report.fyo.singles.SystemSettings?.displayPrecision as number) ?? 2;
const reportData = report.reportData; const reportData = report.reportData;
const columns = report.columns!; const columns = report.columns;
const csvdata: unknown[][] = []; const csvdata: unknown[][] = [];
csvdata.push(columns.map((c) => c.label)); csvdata.push(columns.map((c) => c.label));
@ -140,7 +140,7 @@ function convertReportToCSVMatrix(report: Report): unknown[][] {
} }
const csvrow: unknown[] = []; const csvrow: unknown[] = [];
for (const c in row.cells) { for (let c = 0; c < row.cells.length; c++) {
const cell = getValueFromCell(row.cells[c], displayPrecision); const cell = getValueFromCell(row.cells[c], displayPrecision);
csvrow.push(cell); csvrow.push(cell);
} }

View File

@ -13,9 +13,9 @@ export class StockBalance extends StockLedger {
static reportName = 'stock-balance'; static reportName = 'stock-balance';
static isInventory = true; static isInventory = true;
override ascending: boolean = true; override ascending = true;
override referenceType: ReferenceType = 'All'; override referenceType: ReferenceType = 'All';
override referenceName: string = ''; override referenceName = '';
override async _getReportData(force?: boolean): Promise<ReportData> { override async _getReportData(force?: boolean): Promise<ReportData> {
if (this.shouldRefresh || force || !this._rawData?.length) { if (this.shouldRefresh || force || !this._rawData?.length) {

View File

@ -19,11 +19,11 @@ export class StockLedger extends Report {
static reportName = 'stock-ledger'; static reportName = 'stock-ledger';
static isInventory = true; static isInventory = true;
usePagination: boolean = true; usePagination = true;
_rawData?: ComputedStockLedgerEntry[]; _rawData?: ComputedStockLedgerEntry[];
loading: boolean = false; loading = false;
shouldRefresh: boolean = false; shouldRefresh = false;
item?: string; item?: string;
location?: string; location?: string;
@ -52,7 +52,7 @@ export class StockLedger extends Report {
this._setObservers(); this._setObservers();
} }
async setDefaultFilters() { setDefaultFilters() {
if (!this.toDate) { if (!this.toDate) {
this.toDate = DateTime.now().plus({ days: 1 }).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();
@ -91,9 +91,8 @@ export class StockLedger extends Report {
async _setRawData() { async _setRawData() {
const valuationMethod = const valuationMethod =
(this.fyo.singles.InventorySettings?.valuationMethod as this.fyo.singles.InventorySettings?.valuationMethod ??
| ValuationMethod ValuationMethod.FIFO;
| undefined) ?? ValuationMethod.FIFO;
const rawSLEs = await getRawStockLedgerEntries(this.fyo); const rawSLEs = await getRawStockLedgerEntries(this.fyo);
this._rawData = getStockLedgerEntries(rawSLEs, valuationMethod); this._rawData = getStockLedgerEntries(rawSLEs, valuationMethod);
@ -113,7 +112,7 @@ export class StockLedger extends Report {
} }
let i = 0; let i = 0;
for (const idx in rawData) { for (let idx = 0; idx < rawData.length; idx++) {
const row = rawData[idx]; const row = rawData[idx];
if (this.item && row.item !== this.item) { if (this.item && row.item !== this.item) {
continue; continue;

View File

@ -12,7 +12,7 @@ const NAME_FIELD = {
readOnly: true, readOnly: true,
}; };
export function getSchemas(countryCode: string = '-'): Readonly<SchemaMap> { export function getSchemas(countryCode = '-'): Readonly<SchemaMap> {
const builtCoreSchemas = getCoreSchemas(); const builtCoreSchemas = getCoreSchemas();
const builtAppSchemas = getAppSchemas(countryCode); const builtAppSchemas = getAppSchemas(countryCode);
@ -209,14 +209,14 @@ export function getAbstractCombinedSchemas(schemas: SchemaStubMap): SchemaMap {
for (const name of extendingSchemaNames) { for (const name of extendingSchemaNames) {
const extendingSchema = schemas[name] as Schema; const extendingSchema = schemas[name] as Schema;
const abstractSchema = schemas[extendingSchema.extends!] as SchemaStub; const abstractSchema = schemas[extendingSchema.extends!];
schemaMap[name] = getCombined(extendingSchema, abstractSchema) as Schema; schemaMap[name] = getCombined(extendingSchema, abstractSchema) as Schema;
} }
for (const name in abstractSchemaNames) { abstractSchemaNames.forEach((name) => {
delete schemaMap[name]; delete schemaMap[name];
} });
return schemaMap; return schemaMap;
} }

View File

@ -8,6 +8,8 @@ import {
schemaTranslateables, schemaTranslateables,
} from '../utils/translationHelpers'; } from '../utils/translationHelpers';
/* eslint-disable no-console, @typescript-eslint/no-floating-promises */
const translationsFolder = path.resolve(__dirname, '..', 'translations'); const translationsFolder = path.resolve(__dirname, '..', 'translations');
const PATTERN = /(?<!\w)t`([^`]+)`/g; const PATTERN = /(?<!\w)t`([^`]+)`/g;
@ -21,7 +23,7 @@ function shouldIgnore(p: string, ignoreList: string[]): boolean {
async function getFileList( async function getFileList(
root: string, root: string,
ignoreList: string[], ignoreList: string[],
extPattern: RegExp = /\.(js|ts|vue)$/ extPattern = /\.(js|ts|vue)$/
): Promise<string[]> { ): Promise<string[]> {
const contents: string[] = await fs.readdir(root); const contents: string[] = await fs.readdir(root);
const files: string[] = []; const files: string[] = [];
@ -86,7 +88,7 @@ function getTStrings(content: string): Promise<string[]> {
} }
function tStringFinder(content: string): string[] { function tStringFinder(content: string): string[] {
return [...content.matchAll(PATTERN)].map(([_, t]) => { return [...content.matchAll(PATTERN)].map(([, t]) => {
t = getIndexFormat(t); t = getIndexFormat(t);
return getWhitespaceSanitized(t); return getWhitespaceSanitized(t);
}); });

View File

@ -19,4 +19,5 @@ async function run() {
await unlink(dbPath); await unlink(dbPath);
} }
// eslint-disable-next-line @typescript-eslint/no-floating-promises
run(); run();

View File

@ -54,6 +54,8 @@ export function getTestFyo(): Fyo {
const ext = '.spec.ts'; const ext = '.spec.ts';
/* eslint-disable @typescript-eslint/no-misused-promises */
export function setupTestFyo(fyo: Fyo, filename: string) { export function setupTestFyo(fyo: Fyo, filename: string) {
const testName = path.basename(filename, ext); const testName = path.basename(filename, ext);

View File

@ -13,7 +13,7 @@ export function generateCSV(matrix: unknown[][]): string {
return formattedRows.join('\r\n'); return formattedRows.join('\r\n');
} }
function splitCsvBlock(text: string, splitter: string = '\r\n'): string[] { function splitCsvBlock(text: string, splitter = '\r\n'): string[] {
if (!text.endsWith(splitter)) { if (!text.endsWith(splitter)) {
text += splitter; text += splitter;
} }

View File

@ -93,3 +93,15 @@ export abstract class DatabaseDemuxBase {
abstract callBespoke(method: string, ...args: unknown[]): Promise<unknown>; abstract callBespoke(method: string, ...args: unknown[]): Promise<unknown>;
} }
// Return types of Bespoke Queries
export type TopExpenses = { account: string; total: number }[];
export type TotalOutstanding = { total: number; outstanding: number };
export type Cashflow = { inflow: number; outflow: number; yearmonth: string }[];
export type Balance = { balance: number; yearmonth: string }[];
export type IncomeExpense = { income: Balance; expense: Balance };
export type TotalCreditAndDebit = {
account: string;
totalCredit: number;
totalDebit: number;
};

View File

@ -115,6 +115,7 @@ export function invertMap(map: Record<string, string>): Record<string, string> {
} }
export function time<K, T>(func: (...args: K[]) => T, ...args: K[]): T { export function time<K, T>(func: (...args: K[]) => T, ...args: K[]): T {
/* eslint-disable no-console */
const name = func.name; const name = func.name;
console.time(name); console.time(name);
const stuff = func(...args); const stuff = func(...args);
@ -126,6 +127,7 @@ export async function timeAsync<K, T>(
func: (...args: K[]) => Promise<T>, func: (...args: K[]) => Promise<T>,
...args: K[] ...args: K[]
): Promise<T> { ): Promise<T> {
/* eslint-disable no-console */
const name = func.name; const name = func.name;
console.time(name); console.time(name);
const stuff = await func(...args); const stuff = await func(...args);
@ -255,20 +257,20 @@ export function removeAtIndex<T>(array: T[], index: number): T[] {
export function objectForEach<T extends object | unknown>( export function objectForEach<T extends object | unknown>(
obj: T, obj: T,
func: Function func: (arg: unknown) => unknown
) { ) {
if (typeof obj !== 'object' || obj === null) { if (typeof obj !== 'object' || obj === null) {
return func(obj); return func(obj);
} }
const newObj: Record<string, unknown> = {};
for (const key in obj) { for (const key in obj) {
obj[key] = objectForEach(obj[key], func); newObj[key] = objectForEach(obj[key], func);
} }
return func(obj); return func(newObj);
} }
/** /**
* Asserts that `value` is of type T. Use with care. * Asserts that `value` is of type T. Use with care.
*/ */

View File

@ -10,7 +10,7 @@ export const schemaTranslateables = [
'tab', 'tab',
]; ];
export function getIndexFormat(inp: string | string[]) { export function getIndexFormat(inp: string | string[] | unknown) {
/** /**
* converts: * converts:
* ['This is an ', ,' interpolated ',' string.'] and * ['This is an ', ,' interpolated ',' string.'] and
@ -26,7 +26,7 @@ export function getIndexFormat(inp: string | string[]) {
} else if (inp instanceof Array) { } else if (inp instanceof Array) {
snippets = inp; snippets = inp;
} else { } else {
throw new Error(`invalid input ${inp} of type ${typeof inp}`); throw new Error(`invalid input ${String(inp)} of type ${typeof inp}`);
} }
if (snippets === undefined) { if (snippets === undefined) {
@ -43,7 +43,7 @@ export function getIndexFormat(inp: string | string[]) {
str += s; str += s;
return; return;
} }
str += s + '${' + i + '}'; str += s + '${' + String(i) + '}';
}); });
return str; return str;
@ -70,5 +70,5 @@ export function getWhitespaceSanitized(str: string) {
} }
export function getIndexList(str: string) { export function getIndexList(str: string) {
return [...str.matchAll(/\${([^}]+)}/g)].map(([_, i]) => parseInt(i)); return [...str.matchAll(/\${([^}]+)}/g)].map(([, i]) => parseInt(i));
} }

View File

@ -51,6 +51,7 @@ export interface SelectFileReturn {
canceled: boolean; canceled: boolean;
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type PropertyEnum<T extends Record<string, any>> = { export type PropertyEnum<T extends Record<string, any>> = {
[key in keyof Required<T>]: key; [key in keyof Required<T>]: key;
}; };