mirror of
https://github.com/frappe/books.git
synced 2024-12-22 10:58:59 +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:
parent
f2b620f256
commit
d0571a2450
10
.eslintrc.js
10
.eslintrc.js
@ -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',
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
presets: ['@vue/cli-plugin-babel/preset']
|
|
||||||
};
|
|
@ -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);
|
||||||
|
@ -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 (
|
||||||
|
@ -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 ?? ''}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
||||||
|
@ -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}`,
|
||||||
|
@ -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();
|
||||||
|
@ -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)}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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 [];
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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}`);
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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: '' };
|
||||||
}
|
}
|
||||||
|
@ -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';
|
||||||
}
|
}
|
||||||
|
@ -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}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(
|
||||||
|
@ -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)) {
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}`
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -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'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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 }) },
|
||||||
});
|
});
|
||||||
|
@ -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'],
|
||||||
};
|
};
|
||||||
|
@ -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
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -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;
|
||||||
|
@ -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()) {
|
||||||
|
@ -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()) {
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}`
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -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 ?? '';
|
||||||
}
|
}
|
||||||
|
@ -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}`
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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)) {
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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'];
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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)`
|
||||||
);
|
);
|
||||||
|
@ -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[]>;
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
|
@ -19,4 +19,5 @@ async function run() {
|
|||||||
await unlink(dbPath);
|
await unlink(dbPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
run();
|
run();
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
};
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user