2
0
mirror of https://github.com/frappe/books.git synced 2024-12-23 03:19:01 +00:00

refactor models are now js and better file naming for models

This commit is contained in:
Rushabh Mehta 2018-02-16 18:43:46 +05:30
parent cb446ba49f
commit 2422e33ec0
54 changed files with 320 additions and 476 deletions

2
.gitignore vendored
View File

@ -1,7 +1,7 @@
node_modules
.DS_Store
Thumbs.db
test.db
*test.db
*.log
.cache

2
app.js
View File

@ -2,5 +2,5 @@ const server = require('frappejs/server');
server.start({
backend: 'sqllite',
connection_params: {dbPath: 'test.db'}
connectionParams: {dbPath: 'test.db'}
});

View File

@ -14,16 +14,14 @@ module.exports = class Database {
}
async migrate() {
for (let doctype in frappe.modules) {
for (let doctype in frappe.models) {
// check if controller module
if (frappe.modules[doctype].Meta) {
let meta = frappe.getMeta(doctype);
if (!meta.isSingle) {
if (await this.tableExists(doctype)) {
await this.alterTable(doctype);
} else {
await this.createTable(doctype);
}
let meta = frappe.getMeta(doctype);
if (!meta.isSingle) {
if (await this.tableExists(doctype)) {
await this.alterTable(doctype);
} else {
await this.createTable(doctype);
}
}
}
@ -53,7 +51,7 @@ module.exports = class Database {
}
getColumnDefinition(df) {
return `${df.fieldname} ${this.type_map[df.fieldtype]} ${df.required && !df.default ? "not null" : ""} ${df.default ? `default ${df.default}` : ""}`
// return `${df.fieldname} ${this.type_map[df.fieldtype]} ${ ? "PRIMARY KEY" : ""} ${df.required && !df.default ? "NOT NULL" : ""} ${df.default ? `DEFAULT ${df.default}` : ""}`
}
async alterTable(doctype) {
@ -89,7 +87,7 @@ module.exports = class Database {
doc.name = doctype;
} else {
if (!name) {
throw frappe.errors.ValueError('name is mandatory');
throw new frappe.errors.ValueError('name is mandatory');
}
doc = await this.getOne(doctype, name, fields);
}
@ -113,7 +111,7 @@ module.exports = class Database {
async getSingle(doctype) {
let values = await this.getAll({
doctype: 'Single Value',
doctype: 'SingleValue',
fields: ['fieldname', 'value'],
filters: { parent: doctype },
order_by: 'fieldname',
@ -217,7 +215,7 @@ module.exports = class Database {
let value = doc[field.fieldname];
if (value) {
let singleValue = frappe.newDoc({
doctype: 'Single Value',
doctype: 'SingleValue',
parent: doctype,
fieldname: field.fieldname,
value: value
@ -228,7 +226,7 @@ module.exports = class Database {
}
async deleteSingleValues(name) {
// await frappe.db.run('delete from single_value where parent=?', name)
// await frappe.db.run('delete from SingleValue where parent=?', name)
}
prepareChild(parenttype, parent, child, field, idx) {
@ -275,7 +273,7 @@ module.exports = class Database {
// delete children
let tableFields = frappe.getMeta(doctype).getTableFields();
for (let field of tableFields) {
await this.deleteChildren(frappe.slug(field.childtype), name);
await this.deleteChildren(field.childtype, name);
}
}

View File

@ -45,7 +45,7 @@ module.exports = class mysqlDatabase extends Database{
}
async runCreateTableQuery(doctype, columns, values){
const query = `CREATE TABLE IF NOT EXISTS ${frappe.slug(doctype)} (
const query = `CREATE TABLE IF NOT EXISTS ${doctype} (
${columns.join(", ")})`;
return await this.run(query, values);
@ -62,7 +62,7 @@ module.exports = class mysqlDatabase extends Database{
async runAlterTableQuery(doctype) {
await this.run(`ALTER TABLE ${frappe.slug(doctype)} ADD COLUMN ${this.get_column_definition(df)}`, values);
await this.run(`ALTER TABLE ${doctype} ADD COLUMN ${this.get_column_definition(df)}`, values);
}
getOne(doctype, name, fields = '*') {
@ -70,7 +70,7 @@ module.exports = class mysqlDatabase extends Database{
fields = this.prepareFields(fields);
return new Promise((resolve, reject) => {
this.conn.get(`select ${fields} from ${frappe.slug(doctype)}
this.conn.get(`select ${fields} from ${doctype}
where name = ?`, name,
(err, row) => {
resolve(row || {});
@ -86,7 +86,7 @@ module.exports = class mysqlDatabase extends Database{
doc.name = frappe.getRandomName();
}
return await this.run(`insert into ${frappe.slug(doctype)}
return await this.run(`insert into ${doctype}
(${fields.map(field => field.fieldname).join(", ")})
values (${placeholders})`, this.getFormattedValues(fields, doc));
}
@ -99,19 +99,19 @@ module.exports = class mysqlDatabase extends Database{
// additional name for where clause
values.push(doc.name);
return await this.run(`update ${frappe.slug(doctype)}
return await this.run(`update ${doctype}
set ${assigns.join(", ")} where name=?`, values);
}
async runDeleteOtherChildren(field, added) {
await this.run(`delete from ${frappe.slug(field.childtype)}
await this.run(`delete from ${field.childtype}
where
parent = ? and
name not in (${added.slice(1).map(d => '?').join(', ')})`, added);
}
async deleteOne(doctype, name) {
return await this.run(`delete from ${frappe.slug(doctype)} where name=?`, name);
return await this.run(`delete from ${doctype} where name=?`, name);
}
async deleteChildren(parenttype, parent) {
@ -127,7 +127,7 @@ module.exports = class mysqlDatabase extends Database{
let conditions = this.getFilterConditions(filters);
this.conn.all(`select ${fields.join(", ")}
from ${frappe.slug(doctype)}
from ${doctype}
${conditions.conditions ? "where" : ""} ${conditions.conditions}
${order_by ? ("order by " + order_by) : ""} ${order_by ? (order || "asc") : ""}
${limit ? ("limit " + limit) : ""} ${start ? ("offset " + start) : ""}`, conditions.values,

View File

@ -15,7 +15,7 @@ module.exports = class RESTClient {
async insert(doctype, doc) {
doc.doctype = doctype;
let url = this.getURL('/api/resource', frappe.slug(doctype));
let url = this.getURL('/api/resource', doctype);
return await this.fetch(url, {
method: 'POST',
body: JSON.stringify(doc)
@ -23,7 +23,7 @@ module.exports = class RESTClient {
}
async get(doctype, name) {
let url = this.getURL('/api/resource', frappe.slug(doctype), name);
let url = this.getURL('/api/resource', doctype, name);
return await this.fetch(url, {
method: 'GET',
headers: this.getHeaders()
@ -31,7 +31,7 @@ module.exports = class RESTClient {
}
async getAll({ doctype, fields, filters, start, limit, sort_by, order }) {
let url = this.getURL('/api/resource', frappe.slug(doctype));
let url = this.getURL('/api/resource', doctype);
url = url + "?" + this.getQueryString({
fields: JSON.stringify(fields),
@ -49,7 +49,7 @@ module.exports = class RESTClient {
async update(doctype, doc) {
doc.doctype = doctype;
let url = this.getURL('/api/resource', frappe.slug(doctype), doc.name);
let url = this.getURL('/api/resource', doctype, doc.name);
return await this.fetch(url, {
method: 'PUT',
@ -58,7 +58,7 @@ module.exports = class RESTClient {
}
async delete(doctype, name) {
let url = this.getURL('/api/resource', frappe.slug(doctype), name);
let url = this.getURL('/api/resource', doctype, name);
return await this.fetch(url, {
method: 'DELETE',
@ -66,7 +66,7 @@ module.exports = class RESTClient {
}
async deleteMany(doctype, names) {
let url = this.getURL('/api/resource', frappe.slug(doctype));
let url = this.getURL('/api/resource', doctype);
return await this.fetch(url, {
method: 'DELETE',
@ -79,7 +79,7 @@ module.exports = class RESTClient {
}
async getValue(doctype, name, fieldname) {
let url = this.getURL('/api/resource', frappe.slug(doctype), name, fieldname);
let url = this.getURL('/api/resource', doctype, name, fieldname);
return (await this.fetch(url, {
method: 'GET',

View File

@ -29,14 +29,14 @@ module.exports = class sqliteDatabase extends Database {
}
async runCreateTableQuery(doctype, columns, values) {
const query = `CREATE TABLE IF NOT EXISTS ${frappe.slug(doctype)} (
const query = `CREATE TABLE IF NOT EXISTS ${doctype} (
${columns.join(", ")})`;
return await this.run(query, values);
}
getColumnDefinition(df) {
return `${df.fieldname} ${this.type_map[df.fieldtype]} ${df.required && !df.default ? "not null" : ""} ${df.default ? `default ${df.default}` : ""}`
return `${df.fieldname} ${this.type_map[df.fieldtype]} ${ df.fieldname==="name" ? "PRIMARY KEY" : ""} ${df.required && !df.default ? "NOT NULL" : ""} ${df.default ? `DEFAULT ${df.default}` : ""}`
}
async getTableColumns(doctype) {
@ -44,13 +44,13 @@ module.exports = class sqliteDatabase extends Database {
}
async runAlterTableQuery(doctype, field, values) {
await this.run(`ALTER TABLE ${frappe.slug(doctype)} ADD COLUMN ${this.getColumnDefinition(field)}`, values);
await this.run(`ALTER TABLE ${doctype} ADD COLUMN ${this.getColumnDefinition(field)}`, values);
}
getOne(doctype, name, fields = '*') {
fields = this.prepareFields(fields);
return new Promise((resolve, reject) => {
this.conn.get(`select ${fields} from ${frappe.slug(doctype)}
this.conn.get(`select ${fields} from ${doctype}
where name = ?`, name,
(err, row) => {
resolve(row || {});
@ -66,7 +66,7 @@ module.exports = class sqliteDatabase extends Database {
doc.name = frappe.getRandomName();
}
return await this.run(`insert into ${frappe.slug(doctype)}
return await this.run(`insert into ${doctype}
(${fields.map(field => field.fieldname).join(", ")})
values (${placeholders})`, this.getFormattedValues(fields, doc));
}
@ -79,21 +79,21 @@ module.exports = class sqliteDatabase extends Database {
// additional name for where clause
values.push(doc.name);
return await this.run(`update ${frappe.slug(doctype)}
return await this.run(`update ${doctype}
set ${assigns.join(", ")} where name=?`, values);
}
async runDeleteOtherChildren(field, added) {
// delete other children
// `delete from doctype where parent = ? and name not in (?, ?, ?)}`
await this.run(`delete from ${frappe.slug(field.childtype)}
await this.run(`delete from ${field.childtype}
where
parent = ? and
name not in (${added.slice(1).map(d => '?').join(', ')})`, added);
}
async deleteOne(doctype, name) {
return await this.run(`delete from ${frappe.slug(doctype)} where name=?`, name);
return await this.run(`delete from ${doctype} where name=?`, name);
}
async deleteChildren(parenttype, parent) {
@ -101,7 +101,7 @@ module.exports = class sqliteDatabase extends Database {
}
async deleteSingleValues(name) {
await frappe.db.run('delete from single_value where parent=?', name)
await frappe.db.run('delete from SingleValue where parent=?', name)
}
getAll({ doctype, fields, filters, start, limit, order_by = 'modified', order = 'desc' } = {}) {
@ -111,7 +111,7 @@ module.exports = class sqliteDatabase extends Database {
return new Promise((resolve, reject) => {
let conditions = this.getFilterConditions(filters);
let query = `select ${fields.join(", ")}
from ${frappe.slug(doctype)}
from ${doctype}
${conditions.conditions ? "where" : ""} ${conditions.conditions}
${order_by ? ("order by " + order_by) : ""} ${order_by ? (order || "asc") : ""}
${limit ? ("limit " + limit) : ""} ${start ? ("offset " + start) : ""}`;

View File

@ -4,7 +4,7 @@ const Router = require('frappejs/common/router');
const Page = require('frappejs/client/view/page');
const FormPage = require('frappejs/client/desk/formpage');
const ListPage = require('frappejs/client/desk/listpage');
const Navbar = require('./navbar');
// const Navbar = require('./navbar');
const DeskMenu = require('./menu');
const FormModal = require('frappejs/client/desk/formmodal');

View File

@ -15,7 +15,7 @@ module.exports = class ListPage extends Page {
hasRoute: hasRoute
});
this.list = new (view.getList_class(doctype))({
this.list = new (view.geListClass(doctype))({
doctype: doctype,
parent: this.body,
page: this

View File

@ -8,7 +8,9 @@ module.exports = {
async start({server, columns = 2}) {
window.frappe = frappe;
frappe.init();
common.init_libs(frappe);
frappe.registerLibs(common);
frappe.registerModels(require('frappejs/models'));
frappe.registerModels(require('../models'));
frappe.fetch = window.fetch.bind();
frappe.db = await new RESTClient({server: server});

View File

@ -153,10 +153,15 @@ mark {
.data-table {
.body-scrollable {
border-bottom: 0px !important;
tr:first-child {
.data-table-col {
border-top: 0px !important;
}
}
}
thead td {
border-bottom: 0px !important;
background-color: $gray-200 !important;
}

View File

@ -140,7 +140,7 @@ class BaseControl {
if (this.parentControl) {
// its a child
this.doc[this.fieldname] = value;
await this.parentControl.doc.set(this.fieldname, this.parentControl.getInputValue());
await this.parentControl.doc.applyChange(this.fieldname);
} else {
// parent
await this.doc.set(this.fieldname, value);

View File

@ -11,7 +11,6 @@ class LinkControl extends BaseControl {
setupAwesomplete() {
this.awesomplete = new Awesomplete(this.input, {
autoFirst: true,
minChars: 0,
maxItems: 99,
filter: () => true,
@ -23,7 +22,7 @@ class LinkControl extends BaseControl {
// action to add new item
list.push({
label:frappe._('+ New {0}', this.target),
label: frappe._('+ New {0}', this.label),
value: '__newItem',
});

View File

@ -55,7 +55,11 @@ class TableControl extends BaseControl {
setInputValue(value) {
this.datatable.refresh(this.getTableData(value));
this.datatable.setDimensions();
this.refreshToolbar();
}
refreshToolbar() {
this.wrapper.querySelector('.table-toolbar').classList.toggle('hide', this.disabled ? true : false);
}
getTableData(value) {
@ -124,7 +128,7 @@ class TableControl extends BaseControl {
field: field,
content: field.label,
width: 120,
editable: field.disabled ? false : true,
editable: (this.disabled || field.disabled) ? false : true,
sortable: false,
resizable: true,
dropdown: false,

View File

@ -71,7 +71,7 @@ module.exports = class BaseForm extends Observable {
if (this.meta.settings && this.actions.includes('settings')) {
let menu = this.container.getDropdown(frappe._('Menu'));
menu.addItem(frappe._('Settings...'), () => {
frappe.desk.showFormModal(frappe.slug(this.meta.settings), this.meta.settings);
frappe.desk.showFormModal(this.meta.settings, this.meta.settings);
});
}
@ -99,13 +99,14 @@ module.exports = class BaseForm extends Observable {
}
setTitle() {
const doctype = this.doc.meta.name;
const doctypeLabel = this.doc.meta.label || this.doc.meta.name;
if (this.doc.meta.isSingle || !this.doc.meta.showTitle) {
this.container.setTitle(doctype);
this.container.setTitle(doctypeLabel);
} else if (this.doc._notInserted) {
this.container.setTitle(frappe._('New {0}', doctype));
this.container.setTitle(frappe._('New {0}', doctypeLabel));
} else {
this.container.setTitle(`${doctype} ${this.doc.name}`);
this.container.setTitle(`${doctypeLabel} ${this.doc.name}`);
}
}

View File

@ -1,23 +1,12 @@
const BaseList = require('frappejs/client/view/list');
const BaseForm = require('frappejs/client/view/form');
const frappe = require('frappejs');
module.exports = {
getFormClass(doctype) {
return this.get_view_class(doctype, 'Form', BaseForm);
return (frappe.views['Form'] && frappe.views['Form'][doctype]) || BaseForm;
},
getList_class(doctype) {
return this.get_view_class(doctype, 'List', BaseList);
},
get_view_class(doctype, class_name, default_class) {
let client_module = this.get_client_module(doctype);
if (client_module && client_module[class_name]) {
return client_module[class_name];
} else {
return default_class;
}
},
get_client_module(doctype) {
return frappe.modules[`${doctype}_client`];
geListClass(doctype) {
return (frappe.views['List'] && frappe.views['List'][doctype]) || BaseList;
}
}

View File

@ -77,7 +77,7 @@ module.exports = class BaseList {
makeToolbar() {
this.makeSearch();
this.btnNew = this.page.addButton(frappe._('New'), 'btn-primary', async () => {
await frappe.router.setRoute('new', frappe.slug(this.doctype));
await frappe.router.setRoute('new', this.doctype);
})
this.btnDelete = this.page.addButton(frappe._('Delete'), 'btn-secondary hide', async () => {
await frappe.db.deleteMany(this.doctype, this.getCheckedRowNames());

View File

@ -1,17 +1,17 @@
const utils = require('../utils');
const number_format = require('../utils/number_format');
const format = require('../utils/format');
const model = require('../model');
const _session = require('../session');
const errors = require('./errors');
const BaseDocument = require('frappejs/model/document');
const BaseMeta = require('frappejs/model/meta');
module.exports = {
init_libs(frappe) {
initLibs(frappe) {
Object.assign(frappe, utils);
Object.assign(frappe, number_format);
Object.assign(frappe, format);
frappe.model = model;
frappe._session = _session;
frappe.errors = errors;
frappe.BaseDocument = BaseDocument;
frappe.BaseMeta = BaseMeta;
}
}

View File

@ -1,3 +1,4 @@
module.exports = {
async init() {
if (this._initialized) return;
@ -15,13 +16,29 @@ module.exports = {
initGlobals() {
this.metaCache = {};
this.modules = {};
this.models = {};
this.forms = {};
this.views = {};
this.docs = {};
this.flags = {
cache_docs: false
}
},
registerLibs(common) {
// add standard libs and utils to frappe
common.initLibs(this);
},
registerModels(models) {
Object.assign(this.models, models);
},
registerView(view, doctype, module) {
if (!this.views[view]) this.views[view] = {};
this.views[view][doctype] = module;
},
addToCache(doc) {
if (!this.flags.cache_docs) return;
@ -42,31 +59,32 @@ module.exports = {
getMeta(doctype) {
if (!this.metaCache[doctype]) {
this.metaCache[doctype] = new (this.getMetaClass(doctype))();
let model = this.models[doctype];
if (!model) {
throw `${doctype} is not a registered doctype`;
}
let metaClass = model.metaClass || this.BaseMeta;
this.metaCache[doctype] = new metaClass(model);
}
return this.metaCache[doctype];
},
getMetaClass(doctype) {
doctype = this.slug(doctype);
if (this.modules[doctype] && this.modules[doctype].Meta) {
return this.modules[doctype].Meta;
} else {
return this.BaseMeta;
}
return this.metaCache[doctype];
},
async getDoc(doctype, name) {
let doc = this.getDocFromCache(doctype, name);
if (!doc) {
let controllerClass = this.getControllerClass(doctype);
doc = new controllerClass({doctype:doctype, name: name});
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);
},
@ -85,21 +103,11 @@ module.exports = {
},
newDoc(data) {
let controllerClass = this.getControllerClass(data.doctype);
let doc = new controllerClass(data);
let doc = new (this.getDocumentClass(data.doctype))(data);
doc.setDefaults();
return doc;
},
getControllerClass(doctype) {
doctype = this.slug(doctype);
if (this.modules[doctype] && this.modules[doctype].Document) {
return this.modules[doctype].Document;
} else {
return this.BaseDocument;
}
},
async getNewDoc(doctype) {
let doc = this.newDoc({doctype: doctype});
doc._notInserted = true;
@ -113,10 +121,7 @@ module.exports = {
},
login(user='guest', user_key) {
this.session = new this._session.Session(user);
if (user && user_key) {
this.authenticate(user_key);
}
this.session = {user: user};
},
close() {

View File

@ -1,5 +1,6 @@
const frappe = require('frappejs');
const Observable = require('frappejs/utils/observable');
const model = require('./index');
module.exports = class BaseDocument extends Observable {
constructor(data) {
@ -34,12 +35,16 @@ module.exports = class BaseDocument extends Observable {
// set value and trigger change
async set(fieldname, value) {
this[fieldname] = await this.validateField(fieldname, value);
if (await this.applyFormulae()) {
await this.applyChange(fieldname);
}
async applyChange(fieldname) {
if (await this.applyFormula()) {
// multiple changes
await this.trigger('change', { doc: this });
} else {
// no other change, trigger control refresh
await this.trigger('change', { doc: this, fieldname: fieldname, value: value });
await this.trigger('change', { doc: this, fieldname: fieldname });
}
}
@ -55,9 +60,9 @@ module.exports = class BaseDocument extends Observable {
}
if (this.meta.settings) {
const number_series = (await this.getSettings()).number_series;
if(number_series) {
this.name = await frappe.model.getSeriesNext(number_series);
const numberSeries = (await this.getSettings()).numberSeries;
if(numberSeries) {
this.name = await model.getSeriesNext(numberSeries);
}
}
@ -165,8 +170,8 @@ module.exports = class BaseDocument extends Observable {
}
}
async applyFormulae() {
if (!this.meta.hasFormulae()) {
async applyFormula() {
if (!this.meta.hasFormula()) {
return false;
}
@ -180,7 +185,7 @@ module.exports = class BaseDocument extends Observable {
// for each row
for (let row of this[tablefield.fieldname]) {
for (let field of formulaFields) {
row[field.fieldname] = await eval(field.formula);
row[field.fieldname] = await field.formula(row, doc);
}
}
}
@ -188,7 +193,7 @@ module.exports = class BaseDocument extends Observable {
// parent
for (let field of this.meta.getFormulaFields()) {
doc[field.fieldname] = eval(field.formula);
doc[field.fieldname] = await field.formula(doc);
}
return true;
@ -200,7 +205,7 @@ module.exports = class BaseDocument extends Observable {
this.setStandardValues();
this.setKeywords();
this.setChildIdx();
await this.applyFormulae();
await this.applyFormula();
await this.trigger('validate');
}

View File

@ -4,12 +4,12 @@ module.exports = {
async getSeriesNext(prefix) {
let series;
try {
series = await frappe.getDoc('Number Series', prefix);
series = await frappe.getDoc('NumberSeries', prefix);
} catch (e) {
if (!e.status_code || e.status_code !== 404) {
throw e;
}
series = frappe.newDoc({doctype: 'Number Series', name: prefix, current: 0});
series = frappe.newDoc({doctype: 'NumberSeries', name: prefix, current: 0});
await series.insert();
}
let next = await series.next()

View File

@ -1,5 +1,6 @@
const BaseDocument = require('./document');
const frappe = require('frappejs');
const model = require('./index')
module.exports = class BaseMeta extends BaseDocument {
constructor(data) {
@ -36,21 +37,21 @@ module.exports = class BaseMeta extends BaseDocument {
return this._formulaFields;
}
hasFormulae() {
if (this._hasFormulae===undefined) {
this._hasFormulae = false;
hasFormula() {
if (this._hasFormula===undefined) {
this._hasFormula = false;
if (this.getFormulaFields().length) {
this._hasFormulae = true;
this._hasFormula = true;
} else {
for (let tablefield of this.getTableFields()) {
if (frappe.getMeta(tablefield.childtype).getFormulaFields().length) {
this._hasFormulae = true;
this._hasFormula = true;
break;
}
}
}
}
return this._hasFormulae;
return this._hasFormula;
}
async set(fieldname, value) {
@ -76,7 +77,7 @@ module.exports = class BaseMeta extends BaseDocument {
const doctype_fields = this.fields.map((field) => field.fieldname);
// standard fields
for (let field of frappe.model.common_fields) {
for (let field of model.common_fields) {
if (frappe.db.type_map[field.fieldtype] && !doctype_fields.includes(field.fieldname)) {
_add(field);
}
@ -84,14 +85,14 @@ module.exports = class BaseMeta extends BaseDocument {
if (this.isChild) {
// child fields
for (let field of frappe.model.child_fields) {
for (let field of model.child_fields) {
if (frappe.db.type_map[field.fieldtype] && !doctype_fields.includes(field.fieldname)) {
_add(field);
}
}
} else {
// parent fields
for (let field of frappe.model.parent_fields) {
for (let field of model.parent_fields) {
if (frappe.db.type_map[field.fieldtype] && !doctype_fields.includes(field.fieldname)) {
_add(field);
}

View File

@ -1,5 +1,6 @@
{
"name": "Number Series",
module.exports = {
"name": "NumberSeries",
"documentClass": require('./NumberSeriesDocument.js'),
"doctype": "DocType",
"isSingle": 0,
"isChild": 0,

View File

@ -0,0 +1,15 @@
const BaseDocument = require('frappejs/model/document');
module.exports = class NumberSeries extends BaseDocument {
validate() {
if (this.current===null || this.current===undefined) {
this.current = 0;
}
}
async next() {
this.validate();
this.current++;
await this.update();
return this.current;
}
}

View File

@ -1,5 +1,5 @@
{
"name": "Single Value",
module.exports = {
"name": "SingleValue",
"doctype": "DocType",
"isSingle": 0,
"isChild": 0,

View File

@ -1,5 +1,5 @@
{
"name": "System Settings",
module.exports = {
"name": "SystemSettings",
"doctype": "DocType",
"isSingle": 1,
"isChild": 0,

View File

@ -1,15 +1,11 @@
const BaseList = require('frappejs/client/view/list');
class ToDoList extends BaseList {
module.exports = class ToDoList extends BaseList {
getFields() {
return ['name', 'subject', 'status'];
}
getRowHTML(data) {
let symbol = data.status=="Closed" ? "✔" : "";
return `<a href="#edit/todo/${data.name}">${symbol} ${data.subject}</a>`;
return `<a href="#edit/ToDo/${data.name}">${symbol} ${data.subject}</a>`;
}
}
module.exports = {
List: ToDoList
}

View File

@ -1,5 +1,5 @@
{
"name": "User Role",
module.exports = {
"name": "UserRole",
"doctype": "DocType",
"isSingle": 0,
"isChild": 1,

View File

@ -1,27 +0,0 @@
const BaseMeta = require('frappejs/model/meta');
const BaseDocument = require('frappejs/model/document');
class NumberSeriesMeta extends BaseMeta {
setupMeta() {
Object.assign(this, require('./number_series.json'));
}
}
class NumberSeries extends BaseDocument {
validate() {
if (this.current===null || this.current===undefined) {
this.current = 0;
}
}
async next() {
this.validate();
this.current++;
await this.update();
return this.current;
}
}
module.exports = {
Document: NumberSeries,
Meta: NumberSeriesMeta
};

View File

@ -1,16 +1,15 @@
const BaseMeta = require('frappejs/model/meta');
const BaseDocument = require('frappejs/model/document');
class RoleMeta extends BaseMeta {
setupMeta() {
Object.assign(this, require('./role.json'));
}
}
class Role extends BaseDocument {
}
module.exports = {
Document: Role,
Meta: RoleMeta
};
"name": "Role",
"doctype": "DocType",
"isSingle": 0,
"isChild": 0,
"keywordFields": [],
"fields": [
{
"fieldname": "name",
"label": "Name",
"fieldtype": "Data",
"required": 1
}
]
}

View File

@ -1,15 +0,0 @@
{
"name": "Role",
"doctype": "DocType",
"isSingle": 0,
"isChild": 0,
"keywordFields": [],
"fields": [
{
"fieldname": "name",
"label": "Name",
"fieldtype": "Data",
"required": 1
}
]
}

View File

@ -1,17 +1,21 @@
const BaseMeta = require('frappejs/model/meta');
const BaseDocument = require('frappejs/model/document');
class SessionMeta extends BaseMeta {
setupMeta() {
Object.assign(this, require('./session.json'));
}
}
class Session extends BaseDocument {
}
module.exports = {
Document: Session,
Meta: SessionMeta
};
"name": "Session",
"doctype": "DocType",
"isSingle": 0,
"isChild": 0,
"keywordFields": [],
"fields": [
{
"fieldname": "username",
"label": "Username",
"fieldtype": "Data",
"required": 1
},
{
"fieldname": "password",
"label": "Password",
"fieldtype": "Password",
"required": 1
}
]
}

View File

@ -1,21 +0,0 @@
{
"name": "Session",
"doctype": "DocType",
"isSingle": 0,
"isChild": 0,
"keywordFields": [],
"fields": [
{
"fieldname": "username",
"label": "Username",
"fieldtype": "Data",
"required": 1
},
{
"fieldname": "password",
"label": "Password",
"fieldtype": "Password",
"required": 1
}
]
}

View File

@ -1,16 +0,0 @@
const BaseMeta = require('frappejs/model/meta');
const BaseDocument = require('frappejs/model/document');
class SingleValueMeta extends BaseMeta {
setupMeta() {
Object.assign(this, require('./single_value.json'));
}
}
class SingleValue extends BaseDocument {
}
module.exports = {
Document: SingleValue,
Meta: SingleValueMeta
};

View File

@ -1,16 +0,0 @@
const BaseMeta = require('frappejs/model/meta');
const BaseDocument = require('frappejs/model/document');
class SystemSettingsMeta extends BaseMeta {
setupMeta() {
Object.assign(this, require('./system_settings.json'));
}
}
class SystemSettings extends BaseDocument {
}
module.exports = {
Document: SystemSettings,
Meta: SystemSettingsMeta
};

View File

@ -1,21 +1,34 @@
const BaseMeta = require('frappejs/model/meta');
const BaseDocument = require('frappejs/model/document');
class ToDoMeta extends BaseMeta {
setupMeta() {
Object.assign(this, require('./todo.json'));
}
}
class ToDo extends BaseDocument {
validate() {
if (!this.status) {
this.status = 'Open';
}
}
}
module.exports = {
Document: ToDo,
Meta: ToDoMeta
};
"autoname": "random",
"name": "ToDo",
"doctype": "DocType",
"isSingle": 0,
"keywordFields": [
"subject",
"description"
],
"fields": [
{
"fieldname": "subject",
"label": "Subject",
"fieldtype": "Data",
"required": 1
},
{
"fieldname": "status",
"label": "Status",
"fieldtype": "Select",
"options": [
"Open",
"Closed"
],
"default": "Open",
"required": 1
},
{
"fieldname": "description",
"label": "Description",
"fieldtype": "Text"
}
]
}

View File

@ -1,34 +0,0 @@
{
"autoname": "hash",
"name": "ToDo",
"doctype": "DocType",
"isSingle": 0,
"keywordFields": [
"subject",
"description"
],
"fields": [
{
"fieldname": "subject",
"label": "Subject",
"fieldtype": "Data",
"required": 1
},
{
"fieldname": "status",
"label": "Status",
"fieldtype": "Select",
"options": [
"Open",
"Closed"
],
"default": "Open",
"required": 1
},
{
"fieldname": "description",
"label": "Description",
"fieldtype": "Text"
}
]
}

View File

@ -1,16 +1,30 @@
const BaseMeta = require('frappejs/model/meta');
const BaseDocument = require('frappejs/model/document');
class UserMeta extends BaseMeta {
setupMeta() {
Object.assign(this, require('./user.json'));
}
}
class User extends BaseDocument {
}
module.exports = {
Document: User,
Meta: UserMeta
};
"name": "User",
"doctype": "DocType",
"isSingle": 0,
"isChild": 0,
"keywordFields": [
"name",
"full_name"
],
"fields": [
{
"fieldname": "name",
"label": "Name",
"fieldtype": "Data",
"required": 1
},
{
"fieldname": "full_name",
"label": "Full Name",
"fieldtype": "Data",
"required": 1
},
{
"fieldname": "roles",
"label": "Roles",
"fieldtype": "Table",
"childtype": "UserRole"
}
]
}

View File

@ -1,30 +0,0 @@
{
"name": "User",
"doctype": "DocType",
"isSingle": 0,
"isChild": 0,
"keywordFields": [
"name",
"full_name"
],
"fields": [
{
"fieldname": "name",
"label": "Name",
"fieldtype": "Data",
"required": 1
},
{
"fieldname": "full_name",
"label": "Full Name",
"fieldtype": "Data",
"required": 1
},
{
"fieldname": "roles",
"label": "Roles",
"fieldtype": "Table",
"childtype": "User Role"
}
]
}

View File

@ -1,16 +0,0 @@
const BaseMeta = require('frappejs/model/meta');
const BaseDocument = require('frappejs/model/document');
class UserRoleMeta extends BaseMeta {
setupMeta() {
Object.assign(this, require('./user_role.json'));
}
}
class UserRole extends BaseDocument {
}
module.exports = {
Document: UserRole,
Meta: UserRoleMeta
};

10
models/index.js Normal file
View File

@ -0,0 +1,10 @@
module.exports = {
NumberSeries: require('./doctype/NumberSeries/NumberSeries.js'),
Role: require('./doctype/Role/Role.js'),
Session: require('./doctype/Session/Session.js'),
SingleValue: require('./doctype/SingleValue/SingleValue.js'),
SystemSettings: require('./doctype/SystemSettings/SystemSettings.js'),
ToDo: require('./doctype/ToDo/ToDo.js'),
User: require('./doctype/User/User.js'),
UserRole: require('./doctype/UserRole/UserRole.js')
}

View File

@ -1,24 +1,25 @@
const backends = {};
backends.sqlite = require('frappejs/backends/sqlite');
backends.mysql = require('frappejs/backends/mysql');
//backends.mysql = require('frappejs/backends/mysql');
const express = require('express');
const app = express();
const frappe = require('frappejs');
const rest_api = require('./rest_api')
const init_models = require('frappejs/server/init_models');
const rest_api = require('./rest_api');
const frappeModels = require('frappejs/models');
const common = require('frappejs/common');
const bodyParser = require('body-parser');
const path = require('path');
module.exports = {
async start({backend, connection_params, models_path}) {
async start({backend, connectionParams, models}) {
await this.init();
this.init_models(models_path);
if (models) {
frappe.registerModels(models);
}
// database
await this.init_db({backend:backend, connection_params:connection_params});
await this.initDb({backend:backend, connectionParams:connectionParams});
// app
app.use(bodyParser.json());
@ -33,22 +34,15 @@ module.exports = {
frappe.server = app.listen(frappe.config.port);
},
init_models(models_path) {
// import frappe modules
init_models(path.join(path.dirname(require.resolve('frappejs')), 'models'));
// import modules from the app
init_models(models_path);
},
async init() {
await frappe.init();
common.init_libs(frappe);
frappe.registerModels(frappeModels);
frappe.registerLibs(common);
await frappe.login();
},
async init_db({backend, connection_params}) {
frappe.db = await new backends[backend](connection_params);
async initDb({backend, connectionParams}) {
frappe.db = await new backends[backend](connectionParams);
await frappe.db.connect();
await frappe.db.migrate();
},

View File

@ -1,23 +0,0 @@
const frappe = require('frappejs');
const walk = require('walk');
const path = require('path');
module.exports = function(models_path) {
if (!models_path) {
return;
}
walk.walkSync(models_path, {
listeners: {
file: (basepath, file_data, next) => {
const doctype = path.basename(path.dirname(basepath));
const name = path.basename(basepath);
const file_path = path.resolve(basepath, file_data.name);
if (doctype==='doctype' && file_data.name.endsWith('.js')) {
frappe.modules[file_data.name.slice(0, -3)] = require(file_path);
}
next();
}
}
});
}

View File

@ -1,17 +0,0 @@
const frappe = require('frappejs');
class Session {
constructor(user, user_key) {
this.user = user || 'guest';
if (this.user !== 'guest') {
this.login(user_key);
}
}
login(user_key) {
// could be password, sessionid, otp
}
}
module.exports = { Session: Session };

View File

@ -1,12 +1,15 @@
const server = require('frappejs/server');
const frappe = require('frappejs');
module.exports = {
async init_sqlite() {
async initSqlite({dbPath = '_test.db', models} = {}) {
server.init();
server.init_models();
server.init_db({
if (models) {
frappe.registerModels(models);
}
await server.initDb({
backend: 'sqlite',
connection_params: {dbPath: 'test.db'}
connectionParams: {dbPath: dbPath},
});
}
}

View File

@ -4,7 +4,7 @@ const helpers = require('./helpers');
describe('Controller', () => {
before(async function() {
await helpers.init_sqlite();
await helpers.initSqlite();
});
it('should call controller method', async () => {

View File

@ -4,7 +4,7 @@ const helpers = require('./helpers');
describe('Database', () => {
before(async function() {
await helpers.init_sqlite();
await helpers.initSqlite();
});
it('should insert and get values', async () => {

View File

@ -4,7 +4,7 @@ const helpers = require('./helpers');
describe('Document', () => {
before(async function() {
await helpers.init_sqlite();
await helpers.initSqlite();
});
it('should insert a doc', async () => {
@ -68,8 +68,8 @@ describe('Document', () => {
});
it('should add, fetch and delete documents with children', async() => {
await frappe.newDoc({doctype: 'Role', name: 'Test Role'}).insert();
await frappe.newDoc({doctype: 'Role', name: 'Test Role 1'}).insert();
await frappe.insert({doctype: 'Role', name: 'Test Role'});
await frappe.insert({doctype: 'Role', name: 'Test Role 1'});
let user = frappe.newDoc({
doctype: 'User',

View File

@ -4,7 +4,7 @@ const helpers = require('./helpers');
describe('Meta', () => {
before(async function() {
await helpers.init_sqlite();
await helpers.initSqlite();
});
it('should get init from json file', () => {

View File

@ -4,11 +4,11 @@ const helpers = require('./helpers');
describe('Models', () => {
before(async function() {
await helpers.init_sqlite();
await helpers.initSqlite();
});
it('should get todo json', () => {
let todo = frappe.getMeta('todo');
let todo = frappe.getMeta('ToDo');
assert.equal(todo.isSingle, 0);
});
});

View File

@ -1,10 +1,10 @@
const server = require('frappejs/server');
server.start({
backend: 'mysql',
connection_params: {
host : "test_frappejs",
username : "test_frappejs",
password : "test_frappejs",
db_name : "test_frappejs"
}
});
// const server = require('frappejs/server');
// server.start({
// backend: 'mysql',
// connectionParams: {
// host : "test_frappejs",
// username : "test_frappejs",
// password : "test_frappejs",
// db_name : "test_frappejs"
// }
// });

View File

@ -1,16 +1,17 @@
const assert = require('assert');
const frappe = require('frappejs');
const helpers = require('./helpers');
const model = require('frappejs/model')
describe('Number Series', () => {
describe('NumberSeries', () => {
before(async function() {
await helpers.init_sqlite();
await helpers.initSqlite();
});
it('should start a series and get next value', async () => {
frappe.db.delete('Number Series', 'test-series-')
assert.equal(await frappe.model.getSeriesNext('test-series-'), 'test-series-1');
assert.equal(await frappe.model.getSeriesNext('test-series-'), 'test-series-2');
assert.equal(await frappe.model.getSeriesNext('test-series-'), 'test-series-3');
frappe.db.delete('NumberSeries', 'test-series-')
assert.equal(await model.getSeriesNext('test-series-'), 'test-series-1');
assert.equal(await model.getSeriesNext('test-series-'), 'test-series-2');
assert.equal(await model.getSeriesNext('test-series-'), 'test-series-3');
});
});

View File

@ -17,12 +17,12 @@ describe('Router', () => {
let router = new Router();
router.add('/edit/:doctype', 'catch');
router.add('/edit/:doctype/:name', 'all');
router.add('/edit/todo/:name', 'todo');
router.add('/edit/todo/mytest', 'static');
router.add('/edit/ToDo/:name', 'ToDo');
router.add('/edit/ToDo/mytest', 'static');
assert.equal(router.match('/edit/todo/test').handler, 'todo');
assert.equal(router.match('/edit/ToDo/test').handler, 'ToDo');
assert.equal(router.match('/edit/user/test').handler, 'all');
assert.equal(router.match('/edit/todo/mytest').handler, 'static');
assert.equal(router.match('/edit/ToDo/mytest').handler, 'static');
assert.equal(router.match('/edit/user').handler, 'catch');
});

View File

@ -3,6 +3,6 @@ const server = require('frappejs/server');
if (require.main === module) {
server.start({
backend: 'sqlite',
connection_params: {dbPath: 'test.db'}
connectionParams: {dbPath: 'test.db'}
});
}

View File

@ -4,21 +4,21 @@ const helpers = require('./helpers');
describe('Single Documents', () => {
before(async function() {
await helpers.init_sqlite();
await helpers.initSqlite();
});
it('should set a single value', async () => {
let systemSettings = await frappe.getSingle('System Settings');
let systemSettings = await frappe.getSingle('SystemSettings');
systemSettings.dateFormat = 'dd/mm/yyyy';
await systemSettings.update();
systemSettings = await frappe.getSingle('System Settings');
systemSettings = await frappe.getSingle('SystemSettings');
assert.equal(systemSettings.dateFormat, 'dd/mm/yyyy');
systemSettings.dateFormat = 'mm/dd/yyyy';
await systemSettings.update();
systemSettings = await frappe.getSingle('System Settings');
systemSettings = await frappe.getSingle('SystemSettings');
assert.equal(systemSettings.dateFormat, 'mm/dd/yyyy');
});
});