2
0
mirror of https://github.com/frappe/books.git synced 2024-11-14 01:14:03 +00:00
books/frappe/index.js

392 lines
9.0 KiB
JavaScript
Raw Normal View History

import initLibs from 'frappe/common';
import { getMoneyMaker } from 'pesa';
import { markRaw } from 'vue';
import utils from './utils';
import {
2021-12-23 11:38:32 +00:00
DEFAULT_DISPLAY_PRECISION,
DEFAULT_INTERNAL_PRECISION,
} from './utils/consts';
import Observable from './utils/observable';
import { t, T } from './utils/translation';
class Frappe {
isElectron = false;
isServer = false;
2022-03-16 11:42:06 +00:00
async initializeAndRegister(customModels = {}, force = false) {
2021-12-01 12:51:02 +00:00
this.init(force);
await initLibs(this);
const coreModels = await import('frappe/models');
this.registerModels(coreModels.default);
this.registerModels(customModels);
}
async initializeMoneyMaker(currency) {
currency ??= 'XXX';
// to be called after db initialization
2022-01-07 06:52:40 +00:00
const values =
(await frappe.db?.getSingleValues(
2021-12-23 11:38:32 +00:00
{
fieldname: 'internalPrecision',
parent: 'SystemSettings',
},
{
fieldname: 'displayPrecision',
parent: 'SystemSettings',
}
2022-01-07 06:52:40 +00:00
)) ?? [];
2021-12-23 11:38:32 +00:00
let { internalPrecision: precision, displayPrecision: display } =
values.reduce((acc, { fieldname, value }) => {
acc[fieldname] = value;
return acc;
2021-12-23 11:38:32 +00:00
}, {});
if (typeof precision === 'undefined') {
precision = DEFAULT_INTERNAL_PRECISION;
}
2022-01-07 06:52:40 +00:00
if (typeof precision === 'string') {
precision = parseInt(precision);
}
2021-12-23 11:38:32 +00:00
if (typeof display === 'undefined') {
display = DEFAULT_DISPLAY_PRECISION;
}
if (typeof display === 'string') {
display = parseInt(display);
}
this.pesa = getMoneyMaker({
currency,
precision,
display,
wrapper: markRaw,
});
}
2021-12-01 12:51:02 +00:00
init(force) {
if (this._initialized && !force) return;
// Initialize Config
this.config = {
serverURL: '',
backend: 'sqlite',
port: 8000,
};
// Initialize Globals
this.metaCache = {};
this.models = {};
this.forms = {};
this.views = {};
this.flags = {};
this.methods = {};
this.errorLog = [];
// temp params while calling routes
this.temp = {};
this.params = {};
this.docs = new Observable();
this.events = new Observable();
this._initialized = true;
}
registerModels(models) {
// register models from app/models/index.js
for (let doctype in models) {
let metaDefinition = models[doctype];
if (!metaDefinition.name) {
throw new Error(`Name is mandatory for ${doctype}`);
}
if (metaDefinition.name !== doctype) {
throw new Error(
`Model name mismatch for ${doctype}: ${metaDefinition.name}`
);
}
let fieldnames = (metaDefinition.fields || [])
.map((df) => df.fieldname)
.sort();
let duplicateFieldnames = utils.getDuplicates(fieldnames);
if (duplicateFieldnames.length > 0) {
throw new Error(
`Duplicate fields in ${doctype}: ${duplicateFieldnames.join(', ')}`
);
}
this.models[doctype] = metaDefinition;
}
}
getModels(filterFunction) {
let models = [];
for (let doctype in this.models) {
models.push(this.models[doctype]);
2019-08-01 13:02:19 +00:00
}
return filterFunction ? models.filter(filterFunction) : models;
}
2019-08-01 13:02:19 +00:00
registerView(view, name, module) {
if (!this.views[view]) this.views[view] = {};
this.views[view][name] = module;
}
registerMethod({ method, handler }) {
this.methods[method] = handler;
if (this.app) {
// add to router if client-server
this.app.post(
`/api/method/${method}`,
this.asyncHandler(async function (request, response) {
let data = await handler(request.body);
if (data === undefined) {
data = {};
}
return response.json(data);
})
);
}
}
async call({ method, args }) {
if (this.isServer) {
if (this.methods[method]) {
return await this.methods[method](args);
} else {
2019-10-08 11:16:12 +00:00
throw new Error(`${method} not found`);
}
}
let url = `/api/method/${method}`;
let response = await fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(args || {}),
});
return await response.json();
}
addToCache(doc) {
if (!this.docs) return;
// add to `docs` cache
if (doc.doctype && doc.name) {
if (!this.docs[doc.doctype]) {
this.docs[doc.doctype] = {};
}
this.docs[doc.doctype][doc.name] = doc;
// singles available as first level objects too
if (doc.doctype === doc.name) {
this[doc.name] = doc;
}
// propogate change to `docs`
doc.on('change', (params) => {
this.docs.trigger('change', params);
});
}
}
removeFromCache(doctype, name) {
try {
delete this.docs[doctype][name];
} catch (e) {
console.warn(`Document ${doctype} ${name} does not exist`);
}
}
isDirty(doctype, name) {
return (
(this.docs &&
this.docs[doctype] &&
this.docs[doctype][name] &&
this.docs[doctype][name]._dirty) ||
false
);
}
getDocFromCache(doctype, name) {
if (this.docs && this.docs[doctype] && this.docs[doctype][name]) {
return this.docs[doctype][name];
}
}
getMeta(doctype) {
if (!this.metaCache[doctype]) {
let model = this.models[doctype];
if (!model) {
2019-10-08 11:16:12 +00:00
throw new Error(`${doctype} is not a registered doctype`);
}
let metaClass = model.metaClass || this.BaseMeta;
this.metaCache[doctype] = new metaClass(model);
}
2018-04-30 14:27:41 +00:00
return this.metaCache[doctype];
}
async getDoc(doctype, name, options = { skipDocumentCache: false }) {
let doc = options.skipDocumentCache
? null
: this.getDocFromCache(doctype, name);
if (!doc) {
doc = new (this.getDocumentClass(doctype))({
doctype: doctype,
name: name,
});
await doc.load();
this.addToCache(doc);
}
return doc;
}
getDocumentClass(doctype) {
const meta = this.getMeta(doctype);
return meta.documentClass || this.BaseDocument;
}
async getSingle(doctype) {
return await this.getDoc(doctype, doctype);
}
async getDuplicate(doc) {
const newDoc = await this.getNewDoc(doc.doctype);
for (let field of this.getMeta(doc.doctype).getValidFields()) {
if (['name', 'submitted'].includes(field.fieldname)) continue;
if (field.fieldtype === 'Table') {
newDoc[field.fieldname] = (doc[field.fieldname] || []).map((d) => {
let newd = Object.assign({}, d);
newd.name = '';
return newd;
});
} else {
newDoc[field.fieldname] = doc[field.fieldname];
}
}
return newDoc;
}
getNewDoc(doctype, cacheDoc = true) {
let doc = this.newDoc({ doctype: doctype });
doc._notInserted = true;
doc.name = frappe.getRandomString();
if (cacheDoc) {
this.addToCache(doc);
}
return doc;
}
async newCustomDoc(fields) {
let doc = new this.BaseDocument({ isCustom: 1, fields });
doc._notInserted = true;
doc.name = this.getRandomString();
this.addToCache(doc);
return doc;
}
createMeta(fields) {
let meta = new this.BaseMeta({ isCustom: 1, fields });
return meta;
}
newDoc(data) {
let doc = new (this.getDocumentClass(data.doctype))(data);
doc.setDefaults();
return doc;
}
async insert(data) {
return await this.newDoc(data).insert();
}
async syncDoc(data) {
let doc;
if (await this.db.exists(data.doctype, data.name)) {
doc = await this.getDoc(data.doctype, data.name);
Object.assign(doc, data);
await doc.update();
} else {
doc = this.newDoc(data);
await doc.insert();
}
}
// only for client side
async login(email, password) {
if (email === 'Administrator') {
this.session = {
user: 'Administrator',
};
return;
}
2018-04-30 14:27:41 +00:00
let response = await fetch(this.getServerURL() + '/api/login', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, password }),
});
2018-04-30 14:27:41 +00:00
if (response.status === 200) {
const res = await response.json();
2018-05-07 04:23:20 +00:00
this.session = {
user: email,
token: res.token,
};
2018-05-07 04:23:20 +00:00
return res;
}
2018-05-07 04:23:20 +00:00
return response;
}
async signup(email, fullName, password) {
let 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();
}
2018-04-30 14:27:41 +00:00
return response;
}
2018-04-30 14:27:41 +00:00
getServerURL() {
return this.config.serverURL || '';
}
2018-01-12 12:25:07 +00:00
close() {
this.db.close();
2018-01-12 12:25:07 +00:00
if (this.server) {
this.server.close();
2018-01-12 12:25:07 +00:00
}
}
store = {
2022-03-17 06:03:02 +00:00
isDevelopment: false,
appVersion: '',
};
t = t;
T = T;
}
export { T, t };
export default new Frappe();