2
0
mirror of https://github.com/frappe/books.git synced 2024-11-10 07:40:55 +00:00
books/frappe/core/dbHandler.ts

252 lines
6.6 KiB
TypeScript
Raw Normal View History

import { Frappe } from 'frappe';
import { DatabaseDemux } from 'frappe/demux/db';
2022-03-31 11:43:20 +00:00
import Money from 'pesa/dist/types/src/money';
import { FieldType, FieldTypeEnum, RawValue, SchemaMap } from 'schemas/types';
import { DatabaseBase, DatabaseDemuxBase, GetAllOptions } from 'utils/db/types';
import {
DatabaseDemuxConstructor,
DocValue,
DocValueMap,
RawValueMap,
SingleValue,
} from './types';
2022-03-22 09:28:36 +00:00
// Return types of Bespoke Queries
type TopExpenses = { account: string; total: number }[];
type TotalOutstanding = { total: number; outstanding: number };
type Cashflow = { inflow: number; outflow: number; 'month-year': string }[];
2022-03-31 09:04:30 +00:00
export class DatabaseHandler extends DatabaseBase {
#frappe: Frappe;
#demux: DatabaseDemuxBase;
schemaMap: Readonly<SchemaMap> = {};
2022-03-22 09:28:36 +00:00
constructor(frappe: Frappe, Demux?: DatabaseDemuxConstructor) {
2022-03-31 09:04:30 +00:00
super();
this.#frappe = frappe;
if (Demux !== undefined) {
this.#demux = new Demux(frappe.isElectron);
} else {
this.#demux = new DatabaseDemux(frappe.isElectron);
}
2022-03-22 09:28:36 +00:00
}
2022-03-31 09:04:30 +00:00
async createNewDatabase(dbPath: string, countryCode?: string) {
await this.#demux.createNewDatabase(dbPath, countryCode);
2022-03-22 09:28:36 +00:00
}
2022-03-31 09:04:30 +00:00
async connectToDatabase(dbPath: string, countryCode?: string) {
await this.#demux.connectToDatabase(dbPath, countryCode);
2022-03-22 09:28:36 +00:00
}
2022-03-31 09:04:30 +00:00
async init() {
this.schemaMap = (await this.#demux.getSchemaMap()) as Readonly<SchemaMap>;
2022-03-31 09:04:30 +00:00
}
async insert(
schemaName: string,
docValueMap: DocValueMap
): Promise<DocValueMap> {
2022-03-31 11:43:20 +00:00
let rawValueMap = this.#toRawValueMap(
2022-03-31 09:04:30 +00:00
schemaName,
docValueMap
) as RawValueMap;
rawValueMap = (await this.#demux.call(
'insert',
schemaName,
rawValueMap
)) as RawValueMap;
2022-03-31 11:43:20 +00:00
return this.#toDocValueMap(schemaName, rawValueMap) as DocValueMap;
2022-03-31 09:04:30 +00:00
}
// Read
async get(
schemaName: string,
name: string,
fields?: string | string[]
): Promise<DocValueMap> {
const rawValueMap = (await this.#demux.call(
'get',
schemaName,
name,
fields
)) as RawValueMap;
2022-03-31 11:43:20 +00:00
return this.#toDocValueMap(schemaName, rawValueMap) as DocValueMap;
2022-03-31 09:04:30 +00:00
}
async getAll(
schemaName: string,
options: GetAllOptions = {}
2022-03-31 09:04:30 +00:00
): Promise<DocValueMap[]> {
const rawValueMap = await this.#getAll(schemaName, options);
return this.#toDocValueMap(schemaName, rawValueMap) as DocValueMap[];
}
async getAllRaw(
schemaName: string,
options: GetAllOptions = {}
): Promise<DocValueMap[]> {
return await this.#getAll(schemaName, options);
}
async count(
schemaName: string,
options: GetAllOptions = {}
): Promise<number> {
const rawValueMap = await this.#getAll(schemaName, options);
return rawValueMap.length;
}
async #getAll(
schemaName: string,
options: GetAllOptions = {}
): Promise<RawValueMap[]> {
return (await this.#demux.call(
2022-03-31 09:04:30 +00:00
'getAll',
schemaName,
options
)) as RawValueMap[];
}
async getSingleValues(
...fieldnames: ({ fieldname: string; parent?: string } | string)[]
2022-03-31 11:43:20 +00:00
): Promise<SingleValue<DocValue>> {
const rawSingleValue = (await this.#demux.call(
'getSingleValues',
...fieldnames
)) as SingleValue<RawValue>;
// TODO: Complete this
throw new Error('Not implemented');
2022-03-31 09:04:30 +00:00
}
// Update
async rename(
schemaName: string,
oldName: string,
newName: string
): Promise<void> {
await this.#demux.call('rename', schemaName, oldName, newName);
}
async update(schemaName: string, docValueMap: DocValueMap): Promise<void> {
2022-03-31 11:43:20 +00:00
const rawValueMap = this.#toRawValueMap(schemaName, docValueMap);
2022-03-31 09:04:30 +00:00
await this.#demux.call('update', schemaName, rawValueMap);
}
// Delete
async delete(schemaName: string, name: string): Promise<void> {
await this.#demux.call('delete', schemaName, name);
}
// Other
async close(): Promise<void> {
await this.#demux.call('close');
}
async exists(schemaName: string, name?: string): Promise<boolean> {
return (await this.#demux.call('exists', schemaName, name)) as boolean;
}
/**
* Bespoke function
*
* These are functions to run custom queries that are too complex for
* DatabaseCore and require use of knex or raw queries.
*
* The query logic for these is in backend/database/bespoke.ts
*/
async getTopExpenses(fromDate: string, toDate: string): Promise<TopExpenses> {
return (await this.#demux.callBespoke(
'getTopExpenses',
fromDate,
toDate
)) as TopExpenses;
}
async getTotalOutstanding(
schemaName: string,
fromDate: string,
toDate: string
): Promise<TotalOutstanding> {
return (await this.#demux.callBespoke(
'getTotalOutstanding',
schemaName,
fromDate,
toDate
)) as TotalOutstanding;
}
async getCashflow(fromDate: string, toDate: string): Promise<Cashflow> {
return (await this.#demux.callBespoke(
'getCashflow',
fromDate,
toDate
)) as Cashflow;
}
2022-03-31 11:43:20 +00:00
#toDocValueMap(
2022-03-31 09:04:30 +00:00
schemaName: string,
rawValueMap: RawValueMap | RawValueMap[]
): DocValueMap | DocValueMap[] {
// TODO: Complete this
throw new Error('Not implemented');
}
2022-03-31 11:43:20 +00:00
#toRawValueMap(
2022-03-31 09:04:30 +00:00
schemaName: string,
docValueMap: DocValueMap | DocValueMap[]
): RawValueMap | RawValueMap[] {
// TODO: Complete this
throw new Error('Not implemented');
}
2022-03-31 11:43:20 +00:00
#toDocValue(value: RawValue, fieldtype: FieldType): DocValue {
switch (fieldtype) {
case FieldTypeEnum.Currency:
return this.#frappe.pesa((value ?? 0) as string | number);
case FieldTypeEnum.Date:
return new Date(value as string);
case FieldTypeEnum.Datetime:
return new Date(value as string);
case FieldTypeEnum.Int:
return +(value as string | number);
case FieldTypeEnum.Float:
return +(value as string | number);
case FieldTypeEnum.Check:
return Boolean(value as number);
default:
return String(value);
}
}
#toRawValue(value: DocValue, fieldtype: FieldType): RawValue {
switch (fieldtype) {
case FieldTypeEnum.Currency:
return (value as Money).store;
case FieldTypeEnum.Date:
return (value as Date).toISOString().split('T')[0];
case FieldTypeEnum.Datetime:
return (value as Date).toISOString();
case FieldTypeEnum.Int: {
if (typeof value === 'string') {
return parseInt(value);
}
return Math.floor(value as number);
}
case FieldTypeEnum.Float: {
if (typeof value === 'string') {
return parseFloat(value);
}
return value as number;
}
case FieldTypeEnum.Check:
return Number(value);
default:
return String(value);
}
}
2022-03-22 09:28:36 +00:00
}