2
0
mirror of https://github.com/frappe/books.git synced 2024-11-08 23:00:56 +00:00
books/fyo/core/docHandler.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

205 lines
5.1 KiB
TypeScript
Raw Normal View History

import { Doc } from 'fyo/model/doc';
import { DocMap, ModelMap, SinglesMap } from 'fyo/model/types';
import { coreModels } from 'fyo/models';
import { NotFoundError, ValueError } from 'fyo/utils/errors';
import Observable from 'fyo/utils/observable';
2022-04-22 11:02:03 +00:00
import { Schema } from 'schemas/types';
import { getRandomString } from 'utils';
import { Fyo } from '..';
import { DocValueMap, RawValueMap } from './types';
2022-03-22 09:28:36 +00:00
export class DocHandler {
fyo: Fyo;
models: ModelMap = {};
2022-04-18 08:01:41 +00:00
singles: SinglesMap = {};
docs: Observable<DocMap | undefined> = new Observable();
observer: Observable<never> = new Observable();
#temporaryNameCounters: Record<string, number>;
2022-03-22 09:28:36 +00:00
constructor(fyo: Fyo) {
this.fyo = fyo;
this.#temporaryNameCounters = {};
2022-03-22 09:28:36 +00:00
}
init() {
this.models = {};
2022-04-22 11:02:03 +00:00
this.singles = {};
2022-03-22 09:28:36 +00:00
this.docs = new Observable();
this.observer = new Observable();
2022-03-22 09:28:36 +00:00
}
purgeCache() {
2022-04-22 11:02:03 +00:00
this.init();
}
registerModels(models: ModelMap, regionalModels: ModelMap = {}) {
for (const schemaName in this.fyo.db.schemaMap) {
if (coreModels[schemaName] !== undefined) {
this.models[schemaName] = coreModels[schemaName];
} else if (regionalModels[schemaName] !== undefined) {
this.models[schemaName] = regionalModels[schemaName];
} else if (models[schemaName] !== undefined) {
this.models[schemaName] = models[schemaName];
} else {
this.models[schemaName] = Doc;
}
2022-03-22 09:28:36 +00:00
}
}
/**
* Doc Operations
*/
async getDoc(
schemaName: string,
name?: string,
2022-03-22 09:28:36 +00:00
options = { skipDocumentCache: false }
) {
if (name === undefined) {
name = schemaName;
}
if (name === schemaName && !this.fyo.schemaMap[schemaName]?.isSingle) {
throw new ValueError(`${schemaName} is not a Single Schema`);
}
let doc: Doc | undefined;
2022-03-22 09:28:36 +00:00
if (!options?.skipDocumentCache) {
doc = this.#getFromCache(schemaName, name);
2022-03-22 09:28:36 +00:00
}
if (doc) {
return doc;
}
doc = this.getNewDoc(schemaName, { name }, false);
2022-03-22 09:28:36 +00:00
await doc.load();
this.#addToCache(doc);
2022-03-22 09:28:36 +00:00
return doc;
}
2022-04-22 11:02:03 +00:00
getNewDoc(
schemaName: string,
data: DocValueMap | RawValueMap = {},
cacheDoc = true,
2022-04-22 11:02:03 +00:00
schema?: Schema,
Model?: typeof Doc,
isRawValueMap = true
2022-04-22 11:02:03 +00:00
): Doc {
2022-04-22 12:01:04 +00:00
if (!this.models[schemaName] && Model) {
this.models[schemaName] = Model;
}
Model ??= this.models[schemaName];
2022-04-22 11:02:03 +00:00
schema ??= this.fyo.schemaMap[schemaName];
if (schema === undefined) {
throw new NotFoundError(`Schema not found for ${schemaName}`);
}
const doc = new Model!(schema, data, this.fyo, isRawValueMap);
doc.name ??= this.getTemporaryName(schema);
if (cacheDoc) {
this.#addToCache(doc);
}
2022-03-22 09:28:36 +00:00
return doc;
}
isTemporaryName(name: string, schema: Schema): boolean {
const label = schema.label ?? schema.name;
const template = this.fyo.t`New ${label} `;
return name.includes(template);
}
getTemporaryName(schema: Schema): string {
if (schema.naming === 'random') {
return getRandomString();
}
this.#temporaryNameCounters[schema.name] ??= 1;
const idx = this.#temporaryNameCounters[schema.name];
this.#temporaryNameCounters[schema.name] = idx + 1;
const label = schema.label ?? schema.name;
return this.fyo.t`New ${label} ${String(idx).padStart(2, '0')}`;
}
/**
* Cache operations
*/
#addToCache(doc: Doc) {
if (!doc.name) {
2022-03-22 09:28:36 +00:00
return;
}
const name = doc.name;
const schemaName = doc.schemaName;
if (!this.docs[schemaName]) {
this.docs.set(schemaName, {});
this.#setCacheUpdationListeners(schemaName);
}
this.docs.get(schemaName)![name] = doc;
// singles available as first level objects too
if (schemaName === doc.name) {
this.singles[name] = doc;
2022-03-22 09:28:36 +00:00
}
// propagate change to `docs`
doc.on('change', (params: unknown) => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.docs.trigger('change', params);
});
doc.on('afterSync', () => {
2023-04-14 07:57:44 +00:00
if (doc.name === name && this.#cacheHas(schemaName, name)) {
return;
}
this.removeFromCache(doc.schemaName, name);
this.#addToCache(doc);
});
}
#setCacheUpdationListeners(schemaName: string) {
this.fyo.db.observer.on(`delete:${schemaName}`, (name) => {
if (typeof name !== 'string') {
return;
}
this.removeFromCache(schemaName, name);
});
this.fyo.db.observer.on(`rename:${schemaName}`, (names) => {
const { oldName } = names as { oldName: string };
const doc = this.#getFromCache(schemaName, oldName);
if (doc === undefined) {
return;
}
this.removeFromCache(schemaName, oldName);
this.#addToCache(doc);
});
}
removeFromCache(schemaName: string, name: string) {
const docMap = this.docs.get(schemaName);
delete docMap?.[name];
}
#getFromCache(schemaName: string, name: string): Doc | undefined {
const docMap = this.docs.get(schemaName);
return docMap?.[name];
}
2023-04-14 07:57:44 +00:00
#cacheHas(schemaName: string, name: string): boolean {
return !!this.#getFromCache(schemaName, name);
}
2022-03-22 09:28:36 +00:00
}