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

added singles

This commit is contained in:
Rushabh Mehta 2018-02-12 17:31:31 +05:30
parent 95cbd8ea61
commit 92355be627
23 changed files with 308 additions and 80 deletions

View File

@ -17,12 +17,14 @@ module.exports = class Database {
for (let doctype in frappe.modules) {
// 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);
}
}
}
}
await this.commit();
@ -33,9 +35,9 @@ module.exports = class Database {
let columns = [];
let values = [];
for (let df of meta.getValidFields({ with_children: false })) {
if (this.type_map[df.fieldtype]) {
columns.push(this.getColumnDefinition(df));
for (let field of meta.getValidFields({ withChildren: false })) {
if (this.type_map[field.fieldtype]) {
columns.push(this.getColumnDefinition(field));
}
}
@ -60,7 +62,7 @@ module.exports = class Database {
let meta = frappe.getMeta(doctype);
let values = [];
for (let field of meta.getValidFields({ with_children: false })) {
for (let field of meta.getValidFields({ withChildren: false })) {
if (!tableColumns.includes(field.fieldname) && this.type_map[field.fieldtype]) {
values = []
if (field.default) {
@ -79,12 +81,25 @@ module.exports = class Database {
// alter table {doctype} add column ({column_def});
}
async get(doctype, name, fields = '*') {
// load parent
let doc = await this.getOne(doctype, name, fields);
async get(doctype, name=null, fields = '*') {
let meta = frappe.getMeta(doctype);
let doc;
if (meta.isSingle) {
doc = await this.getSingle(doctype);
doc.name = doctype;
} else {
if (!name) {
throw frappe.errors.ValueError('name is mandatory');
}
doc = await this.getOne(doctype, name, fields);
}
await this.loadChildren(doc, meta);
return doc;
}
async loadChildren(doc, meta) {
// load children
let tableFields = frappe.getMeta(doctype).getTableFields();
let tableFields = meta.getTableFields();
for (let field of tableFields) {
doc[field.fieldname] = await this.getAll({
doctype: field.childtype,
@ -94,6 +109,20 @@ module.exports = class Database {
order: 'asc'
});
}
}
async getSingle(doctype) {
let values = await this.getAll({
doctype: 'Single Value',
fields: ['fieldname', 'value'],
filters: { parent: doctype },
order_by: 'fieldname',
order: 'asc'
});
let doc = {};
for (let row of values) {
doc[row.fieldname] = row.value;
}
return doc;
}
@ -109,11 +138,23 @@ module.exports = class Database {
}
async insert(doctype, doc) {
let meta = frappe.getMeta(doctype);
// insert parent
if (meta.isSingle) {
await this.updateSingle(meta, doc, doctype);
} else {
await this.insertOne(doctype, doc);
}
// insert children
let tableFields = frappe.getMeta(doctype).getTableFields();
await this.insertChildren(meta, doc, doctype);
return doc;
}
async insertChildren(meta, doc, doctype) {
let tableFields = meta.getTableFields();
for (let field of tableFields) {
let idx = 0;
for (let child of (doc[field.fieldname] || [])) {
@ -122,8 +163,6 @@ module.exports = class Database {
idx++;
}
}
return doc;
}
async insertOne(doctype, doc) {
@ -131,28 +170,37 @@ module.exports = class Database {
}
async update(doctype, doc) {
let meta = frappe.getMeta(doctype);
// update parent
if (meta.isSingle) {
await this.updateSingle(meta, doc, doctype);
} else {
await this.updateOne(doctype, doc);
}
// insert or update children
let tableFields = frappe.getMeta(doctype).getTableFields();
for (let field of tableFields) {
await this.updateChildren(meta, doc, doctype);
return doc;
}
async updateChildren(meta, doc, doctype) {
let tableFields = meta.getTableFields();
for (let field of tableFields) {
// first key is "parent" - for SQL params
let added = [doc.name];
for (let child of (doc[field.fieldname] || [])) {
this.prepareChild(doctype, doc.name, child, field, added.length - 1);
if (await this.exists(field.childtype, child.name)) {
await this.updateOne(field.childtype, child);
} else {
}
else {
await this.insertOne(field.childtype, child);
}
added.push(child.name);
}
await this.runDeleteOtherChildren(field, added);
}
return doc;
}
async updateOne(doctype, doc) {
@ -163,6 +211,26 @@ module.exports = class Database {
// delete from doctype where parent = ? and name not in (?, ?, ?)
}
async updateSingle(meta, doc, doctype) {
await this.deleteSingleValues();
for (let field of meta.getValidFields({withChildren: false})) {
let value = doc[field.fieldname];
if (value) {
let singleValue = frappe.newDoc({
doctype: 'Single Value',
parent: doctype,
fieldname: field.fieldname,
value: value
})
await singleValue.insert();
}
}
}
async deleteSingleValues(name) {
// await frappe.db.run('delete from single_value where parent=?', name)
}
prepareChild(parenttype, parent, child, field, idx) {
if (!child.name) {
child.name = frappe.getRandomName();
@ -174,7 +242,7 @@ module.exports = class Database {
}
getKeys(doctype) {
return frappe.getMeta(doctype).getValidFields({ with_children: false });
return frappe.getMeta(doctype).getValidFields({ withChildren: false });
}
getFormattedValues(fields, doc) {

View File

@ -97,7 +97,11 @@ module.exports = class sqliteDatabase extends Database {
}
async deleteChildren(parenttype, parent) {
await this.run(`delete from ${parent} where parent=?`, parent);
await this.run(`delete from ${parenttype} where parent=?`, parent);
}
async deleteSingleValues(name) {
await frappe.db.run('delete from single_value where parent=?', name)
}
getAll({ doctype, fields, filters, start, limit, order_by = 'modified', order = 'desc' } = {}) {
@ -106,12 +110,13 @@ module.exports = class sqliteDatabase extends Database {
}
return new Promise((resolve, reject) => {
let conditions = this.getFilterConditions(filters);
this.conn.all(`select ${fields.join(", ")}
let query = `select ${fields.join(", ")}
from ${frappe.slug(doctype)}
${conditions.conditions ? "where" : ""} ${conditions.conditions}
${order_by ? ("order by " + order_by) : ""} ${order_by ? (order || "asc") : ""}
${limit ? ("limit " + limit) : ""} ${start ? ("offset " + start) : ""}`, conditions.values,
${limit ? ("limit " + limit) : ""} ${start ? ("offset " + start) : ""}`;
this.conn.all(query, conditions.values,
(err, rows) => {
if (err) {
reject(err);

View File

@ -59,17 +59,23 @@ module.exports = {
async getDoc(doctype, name) {
let doc = this.getDocFromCache(doctype, name);
if (!doc) {
let controller_class = this.getControllerClass(doctype);
doc = new controller_class({doctype:doctype, name: name});
let controllerClass = this.getControllerClass(doctype);
doc = new controllerClass({doctype:doctype, name: name});
await doc.load();
this.addToCache(doc);
}
return doc;
},
async getSingle(doctype) {
return await this.getDoc(doctype, doctype);
},
newDoc(data) {
let controller_class = this.getControllerClass(data.doctype);
return new controller_class(data);
let controllerClass = this.getControllerClass(data.doctype);
let doc = new controllerClass(data);
doc.setDefaults();
return doc;
},
getControllerClass(doctype) {
@ -83,8 +89,8 @@ module.exports = {
async getNewDoc(doctype) {
let doc = this.newDoc({doctype: doctype});
doc.setName();
doc._notInserted = true;
doc.name = this.getRandomName();
this.addToCache(doc);
return doc;
},

View File

@ -9,9 +9,9 @@ module.exports = {
fs.writeFileSync(`./models/doctype/${utils.slug(name)}/${utils.slug(name)}.json`, `{
"name": "${name}",
"doctype": "DocType",
"is_single": 0,
"is_child": 0,
"keyword_fields": [],
"isSingle": 0,
"isChild": 0,
"keywordFields": [],
"fields": [
{
"fieldname": "name",

View File

@ -23,6 +23,20 @@ module.exports = class BaseDocument {
this.handlers[key].push(method || key);
}
get meta() {
if (!this._meta) {
this._meta = frappe.getMeta(this.doctype);
}
return this._meta;
}
async getSettings() {
if (!this._settings) {
this._settings = await frappe.getSingle(this.meta.settings);
}
return this._settings;
}
get(fieldname) {
return this[fieldname];
}
@ -39,7 +53,22 @@ module.exports = class BaseDocument {
}
}
setName() {
async setName() {
// name === doctype for Single
if (this.meta.isSingle) {
this.name = this.meta.name;
return;
}
if (this.meta.settings) {
const number_series = (await this.getSettings()).number_series;
console.log(1, number_series);
if(number_series) {
this.name = await frappe.model.getSeriesNext(number_series);
console.log(2, this.name);
}
}
// assign a random name by default
// override this to set a name
if (!this.name) {
@ -47,6 +76,14 @@ module.exports = class BaseDocument {
}
}
setDefaults() {
for (let field of this.meta.fields) {
if (!this[field.fieldname] && field.default) {
this[field.fieldname] = field.default;
}
}
}
setKeywords() {
let keywords = [];
for (let fieldname of this.meta.getKeywordFields()) {
@ -55,13 +92,6 @@ module.exports = class BaseDocument {
this.keywords = keywords.join(', ');
}
get meta() {
if (!this._meta) {
this._meta = frappe.getMeta(this.doctype);
}
return this._meta;
}
append(key, document) {
if (!this[key]) {
this[key] = [];
@ -170,7 +200,7 @@ module.exports = class BaseDocument {
async commit() {
// re-run triggers
this.setName();
await this.setName();
this.setStandardValues();
this.setKeywords();
this.setChildIdx();

View File

@ -1,7 +1,7 @@
const frappe = require('frappejs');
module.exports = {
async get_series_next(prefix) {
async getSeriesNext(prefix) {
let series;
try {
series = await frappe.getDoc('Number Series', prefix);

View File

@ -70,15 +70,15 @@ module.exports = class BaseMeta extends BaseDocument {
return this[fieldname];
}
getValidFields({ with_children = true } = {}) {
getValidFields({ withChildren = true } = {}) {
if (!this._valid_fields) {
this._valid_fields = [];
this._valid_fields_with_children = [];
this._valid_fields_withChildren = [];
const _add = (field) => {
this._valid_fields.push(field);
this._valid_fields_with_children.push(field);
this._valid_fields_withChildren.push(field);
}
const doctype_fields = this.fields.map((field) => field.fieldname);
@ -90,7 +90,7 @@ module.exports = class BaseMeta extends BaseDocument {
}
}
if (this.is_child) {
if (this.isChild) {
// child fields
for (let field of frappe.model.child_fields) {
if (frappe.db.type_map[field.fieldtype] && !doctype_fields.includes(field.fieldname)) {
@ -114,22 +114,31 @@ module.exports = class BaseMeta extends BaseDocument {
_add(field);
}
// include tables if (with_children = True)
// include tables if (withChildren = True)
if (!include && field.fieldtype === 'Table') {
this._valid_fields_with_children.push(field);
this._valid_fields_withChildren.push(field);
}
}
}
if (with_children) {
return this._valid_fields_with_children;
if (withChildren) {
return this._valid_fields_withChildren;
} else {
return this._valid_fields;
}
}
getKeywordFields() {
return this.keyword_fields || this.meta.fields.filter(field => field.required).map(field => field.fieldname);
if (!this._keywordFields) {
this._keywordFields = this.keywordFields;
if (!(this._keywordFields && this._keywordFields.length && this.fields)) {
this._keywordFields = this.fields.filter(field => field.required).map(field => field.fieldname);
}
if (!(this._keywordFields && this._keywordFields.length)) {
this._keywordFields = ['name']
}
}
return this._keywordFields;
}
validate_select(field, value) {

View File

@ -8,10 +8,16 @@ class NumberSeriesMeta extends BaseMeta {
}
class NumberSeries extends BaseDocument {
async next() {
setup() {
this.addHandler('validate');
}
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,9 +1,9 @@
{
"name": "Number Series",
"doctype": "DocType",
"is_single": 0,
"is_child": 0,
"keyword_fields": [],
"isSingle": 0,
"isChild": 0,
"keywordFields": [],
"fields": [
{
"fieldname": "name",

View File

@ -1,9 +1,9 @@
{
"name": "Role",
"doctype": "DocType",
"is_single": 0,
"is_child": 0,
"keyword_fields": [],
"isSingle": 0,
"isChild": 0,
"keywordFields": [],
"fields": [
{
"fieldname": "name",

View File

@ -1,9 +1,9 @@
{
"name": "Session",
"doctype": "DocType",
"is_single": 0,
"is_child": 0,
"keyword_fields": [],
"isSingle": 0,
"isChild": 0,
"keywordFields": [],
"fields": [
{
"fieldname": "username",

View File

@ -0,0 +1,16 @@
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

@ -0,0 +1,27 @@
{
"name": "Single Value",
"doctype": "DocType",
"isSingle": 0,
"isChild": 0,
"keywordFields": [],
"fields": [
{
"fieldname": "parent",
"label": "Parent",
"fieldtype": "Data",
"required": 1
},
{
"fieldname": "fieldname",
"label": "Fieldname",
"fieldtype": "Data",
"required": 1
},
{
"fieldname": "value",
"label": "Value",
"fieldtype": "Data",
"required": 1
}
]
}

View File

@ -0,0 +1,16 @@
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

@ -0,0 +1,21 @@
{
"name": "System Settings",
"doctype": "DocType",
"isSingle": 1,
"isChild": 0,
"keywordFields": [],
"fields": [
{
"fieldname": "dateFormat",
"label": "Date Format",
"fieldtype": "Select",
"options": [
"dd/mm/yyyy",
"mm/dd/yyyy",
"dd-mm-yyyy",
"mm-dd-yyyy"
],
"required": 1
}
]
}

View File

@ -2,8 +2,8 @@
"autoname": "hash",
"name": "ToDo",
"doctype": "DocType",
"is_single": 0,
"keyword_fields": [
"isSingle": 0,
"keywordFields": [
"subject",
"description"
],

View File

@ -1,9 +1,9 @@
{
"name": "User",
"doctype": "DocType",
"is_single": 0,
"is_child": 0,
"keyword_fields": [
"isSingle": 0,
"isChild": 0,
"keywordFields": [
"name",
"full_name"
],

View File

@ -1,9 +1,9 @@
{
"name": "User Role",
"doctype": "DocType",
"is_single": 0,
"is_child": 1,
"keyword_fields": [],
"isSingle": 0,
"isChild": 1,
"keywordFields": [],
"fields": [
{
"fieldname": "role",

View File

@ -4,7 +4,7 @@ module.exports = {
setup(app) {
// get list
app.get('/api/resource/:doctype', frappe.async_handler(async function(request, response) {
for (key of ['fields', 'filters']) {
for (let key of ['fields', 'filters']) {
if (request.query[key]) {
request.query[key] = JSON.parse(request.query[key]);
}

View File

@ -9,7 +9,7 @@ describe('Meta', () => {
it('should get init from json file', () => {
let todo = frappe.getMeta('ToDo');
assert.equal(todo.is_single, 0);
assert.equal(todo.isSingle, 0);
});
it('should get fields from meta', () => {

View File

@ -9,6 +9,6 @@ describe('Models', () => {
it('should get todo json', () => {
let todo = frappe.getMeta('todo');
assert.equal(todo.is_single, 0);
assert.equal(todo.isSingle, 0);
});
});

View File

@ -9,8 +9,8 @@ describe('Number Series', () => {
it('should start a series and get next value', async () => {
frappe.db.delete('Number Series', 'test-series-')
assert.equal(await frappe.model.get_series_next('test-series-'), 'test-series-1');
assert.equal(await frappe.model.get_series_next('test-series-'), 'test-series-2');
assert.equal(await frappe.model.get_series_next('test-series-'), 'test-series-3');
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');
});
});

24
tests/test_single.js Normal file
View File

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