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

Merge pull request #111 from frappe/patch-run

feat: Patch Run
This commit is contained in:
Faris Ansari 2020-01-28 17:13:40 +05:30 committed by GitHub
commit 6076b4f966
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 93 additions and 256 deletions

View File

@ -208,7 +208,7 @@ module.exports = {
return newDoc;
},
async getNewDoc(doctype) {
getNewDoc(doctype) {
let doc = this.newDoc({ doctype: doctype });
doc._notInserted = true;
doc.name = frappe.getRandomString();

View File

@ -153,13 +153,27 @@ module.exports = class BaseMeta extends BaseDocument {
this._validFieldsWithChildren.push(field);
};
const doctype_fields = this.fields.map(field => field.fieldname);
// fields validation
this.fields.forEach((df, i) => {
if (!df.fieldname) {
throw new frappe.errors.ValidationError(
`DocType ${this.name}: "fieldname" is required for field at index ${i}`
);
}
if (!df.fieldtype) {
throw new frappe.errors.ValidationError(
`DocType ${this.name}: "fieldtype" is required for field "${df.fieldname}"`
);
}
});
const doctypeFields = this.fields.map(field => field.fieldname);
// standard fields
for (let field of model.commonFields) {
if (
frappe.db.typeMap[field.fieldtype] &&
!doctype_fields.includes(field.fieldname)
!doctypeFields.includes(field.fieldname)
) {
_add(field);
}
@ -178,7 +192,7 @@ module.exports = class BaseMeta extends BaseDocument {
for (let field of model.childFields) {
if (
frappe.db.typeMap[field.fieldtype] &&
!doctype_fields.includes(field.fieldname)
!doctypeFields.includes(field.fieldname)
) {
_add(field);
}
@ -188,7 +202,7 @@ module.exports = class BaseMeta extends BaseDocument {
for (let field of model.parentFields) {
if (
frappe.db.typeMap[field.fieldtype] &&
!doctype_fields.includes(field.fieldname)
!doctypeFields.includes(field.fieldname)
) {
_add(field);
}
@ -200,7 +214,7 @@ module.exports = class BaseMeta extends BaseDocument {
for (let field of model.treeFields) {
if (
frappe.db.typeMap[field.fieldtype] &&
!doctype_fields.includes(field.fieldname)
!doctypeFields.includes(field.fieldname)
) {
_add(field);
}
@ -270,7 +284,8 @@ module.exports = class BaseMeta extends BaseDocument {
}
if (!validValues.includes(value)) {
throw new frappe.errors.ValueError(
`Invalid value "${value}" for "${field.label}". Must be one of ${options.join(', ')}`
// prettier-ignore
`DocType ${this.name}: Invalid value "${value}" for "${field.label}". Must be one of ${options.join(', ')}`
);
}
return value;

40
model/migrate.js Normal file
View File

@ -0,0 +1,40 @@
const frappe = require('frappejs');
module.exports = async function migrate(allPatches, patchOrder) {
let executedPatchRuns = [];
try {
executedPatchRuns = (
await frappe.db.getAll({ doctype: 'PatchRun', fields: ['name'] })
).map(d => d.name);
} catch (error) {}
let patchRunOrder = patchOrder
.map(text => {
let [patch] = text.split(' ');
if (text && patch) {
return {
fileName: text,
method: allPatches[patch]
};
}
})
.filter(Boolean);
for (let patch of patchRunOrder) {
if (!executedPatchRuns.includes(patch.fileName)) {
await runPatch(patch);
}
}
};
async function runPatch(patch) {
try {
await patch.method();
let patchRun = frappe.getNewDoc('PatchRun');
patchRun.name = patch.fileName;
await patchRun.insert();
} catch (error) {
console.error(error);
console.log('Could not run patch', patch);
}
}

View File

@ -1,28 +0,0 @@
module.exports = {
name: "FilterGroup",
isSingle: 0,
isChild: 0,
keywordFields: [],
fields: [
{
fieldname: "name",
label: "Name",
fieldtype: "Data",
required: 1
},
{
fieldname: "forDocType",
label: "Document Type",
fieldtype: "Data",
required: 1,
disabled: 1,
},
{
fieldname: "items",
fieldtype: "Table",
childtype: "FilterItem",
label: "Items",
required: 1
}
]
}

View File

@ -1,36 +0,0 @@
module.exports = {
name: "FilterItem",
doctype: "DocType",
isSingle: 0,
isChild: 1,
keywordFields: [],
fields: [
{
fieldname: "field",
label: "Field",
fieldtype: "Select",
required: 1
},
{
fieldname: "condition",
label: "Condition",
fieldtype: "Select",
options: [
'Equals',
'>',
'<',
'>=',
'<=',
'Between',
'Includes',
'One Of'
],
required: 1
},
{
fieldname: "value",
label: "Value",
fieldtype: "Data",
}
]
}

View File

@ -1,79 +0,0 @@
const frappe = require('frappejs');
module.exports = {
name: "FilterSelector",
label: "Set Filters",
documentClass: require('./FilterSelectorDocument'),
isSingle: 1,
isChild: 0,
keywordFields: [],
fields: [
{
fieldname: "forDocType",
label: "Document Type",
fieldtype: "Data",
hidden: 1,
},
{
fieldname: "filterGroup",
label: "Saved Filters",
fieldtype: "Link",
target: "FilterGroup",
getFilters: (query, control) => {
return {
forDocType: control.doc.forDocType,
keywords: ["like", query]
}
}
},
{
fieldname: "filterGroupName",
label: "New Filter Name",
fieldtype: "Data",
},
{
fieldname: "items",
label: "Items",
fieldtype: "Table",
childtype: "FilterItem",
neverEmpty: 1,
// copy items from saved filter group
formula: async (doc) => {
if (doc._lastFilterGroup !== doc.filterGroup) {
// fitler changed
if (doc.filterGroup) {
doc.items = [];
const filterGroup = await frappe.getDoc('FilterGroup', doc.filterGroup);
// copy items
for(let source of filterGroup.items) {
const item = Object.assign({}, source);
item.parent = item.name = '';
doc.items.push(item);
}
} else {
// no filter group selected
doc.items = [{idx: 0}];
}
doc._lastFilterGroup = doc.filterGroup;
}
return false;
},
}
],
formEvents: {
// set the fields of the selected item in the 'select'
refresh: (form) => {
// override the `getOptions` method in the `field` property
frappe.getMeta('FilterItem').getField('field').getOptions = () => {
return frappe.getMeta(form.doc.forDocType).fields.map((f) => {
return {label: f.label, value: f.fieldname};
});
}
}
}
}

View File

@ -1,78 +0,0 @@
const BaseDocument = require('frappejs/model/document');
const frappe = require('frappejs');
module.exports = class FormSelector extends BaseDocument {
reset(doctype) {
if (doctype) {
this.forDocType = doctype;
}
this.items = [];
this.filterGroup = '';
this.filterGroupName = '';
}
getFilters() {
const filters = {};
for (let item of (this.items || [])) {
filters[item.field] = [(item.condition === 'Equals') ? '=' : item.condition,
item.value];
}
return filters;
}
setFilters(filters) {
this.reset();
for (let key in filters) {
let value = filters[key];
if (value instanceof Array) {
this.items.push({field: key, condition: value[0], value: value[1]});
} else {
this.items.push({field: key, condition: 'Equals', value: value});
}
}
}
getText() {
if (this.items && this.items.length) {
this.forMeta = frappe.getMeta(this.forDocType);
return this.items.map(v => `${this.forMeta.getLabel(v.field)} ${v.condition} ${v.value}`).join(', ');
} else {
return 'Set Filters';
}
}
async update() {
// save new group filter
if (frappe.isServer) {
if (this.filterGroupName) {
await this.makeFilterGroup();
} else if (this.filterGroup) {
await this.updateFilterGroup();
}
return this;
} else {
return super.update();
}
}
async makeFilterGroup() {
const filterGroup = frappe.newDoc({doctype:'FilterGroup'});
filterGroup.name = this.filterGroupName;
this.updateFilterGroupValues(filterGroup);
await filterGroup.insert();
}
async updateFilterGroup() {
const filterGroup = await frappe.getDoc('FilterGroup', this.filterGroup);
this.updateFilterGroupValues(filterGroup);
await filterGroup.update();
}
updateFilterGroupValues(filterGroup) {
filterGroup.forDocType = this.forDocType;
filterGroup.items = [];
for (let item of this.items) {
filterGroup.items.push({field: item.field, condition: item.condition, value: item.value});
}
}
}

View File

@ -1,14 +0,0 @@
const BaseForm = require('frappejs/client/view/form');
const frappe = require('frappejs');
module.exports = class FilterSelectorForm extends BaseForm {
makeSaveButton() {
this.saveButton = this.container.addButton(frappe._("Apply"), 'primary', async (event) => {
if (this.doc.filterGroupName || (this.doc.filterGroup && this.doc._dirty)) {
// new filter, call update
await this.save();
}
this.trigger('apply-filters');
});
}
}

View File

@ -0,0 +1,10 @@
module.exports = {
name: 'PatchRun',
fields: [
{
fieldname: 'name',
fieldtype: 'Data',
label: 'Name'
}
]
};

View File

@ -1,7 +1,4 @@
module.exports = {
FilterItem: require('./doctype/FilterItem/FilterItem.js'),
FilterGroup: require('./doctype/FilterGroup/FilterGroup.js'),
FilterSelector: require('./doctype/FilterSelector/FilterSelector.js'),
NumberSeries: require('./doctype/NumberSeries/NumberSeries.js'),
PrintFormat: require('./doctype/PrintFormat/PrintFormat.js'),
Role: require('./doctype/Role/Role.js'),
@ -11,5 +8,6 @@ module.exports = {
ToDo: require('./doctype/ToDo/ToDo.js'),
User: require('./doctype/User/User.js'),
UserRole: require('./doctype/UserRole/UserRole.js'),
File: require('./doctype/File/File.js')
File: require('./doctype/File/File.js'),
PatchRun: require('./doctype/PatchRun/PatchRun.js')
};

View File

@ -41,10 +41,8 @@ describe('Database Migrate', () => {
let dbPath = '_migrate_test.db';
server.init();
let models = {
models: {
Person,
Gender
}
Person,
Gender
};
frappe.models = {};
frappe.registerModels(models);
@ -62,12 +60,15 @@ describe('Database Migrate', () => {
// check if tables were created
assert.deepEqual(['Gender', 'Person'], tables);
let fields = await frappe.db.sql('PRAGMA table_info(??)', 'Person')
let fields = await frappe.db.sql('PRAGMA table_info(??)', 'Person');
// check if standard fields and model fields were created
assert.equal(fields.length, 8);
assert.equal(fields.find(d => d.name === 'age').type, 'integer');
let foreignKeys = await frappe.db.sql('PRAGMA foreign_key_list(??)', 'Person')
let foreignKeys = await frappe.db.sql(
'PRAGMA foreign_key_list(??)',
'Person'
);
// check for foreign keys
assert.equal(foreignKeys.length, 1);
assert.equal(foreignKeys[0].from, 'gender');

View File

@ -25,7 +25,15 @@ module.exports = {
dateFormat = frappe.SystemSettings.dateFormat;
}
value = luxon.DateTime.fromISO(value).toFormat(dateFormat);
if (typeof value === 'string') {
// ISO String
value = luxon.DateTime.fromISO(value);
} else if (Object.prototype.toString.call(value) === '[object Date]') {
// JS Date
value = luxon.DateTime.fromJSDate(value);
}
value = value.toFormat(dateFormat);
if (value === 'Invalid DateTime') {
value = '';
}

View File

@ -1,7 +1,7 @@
const fs = require('fs');
const path = require('path');
const chalk = require('chalk');
const deepmerge = require('deepmerge');
const defaultsDeep = require('lodash/defaultsDeep');
const logger = require('./logger');
const frappeConf = 'frappe.conf.js';
@ -30,7 +30,7 @@ function getAppConfig() {
}
}
const appConfig = require(path.resolve(getAppDir(), frappeConf));
return deepmerge(defaults, appConfig);
return defaultsDeep(defaults, appConfig);
}
function resolveAppDir(...args) {
@ -41,4 +41,4 @@ module.exports = {
getAppDir,
getAppConfig,
resolveAppDir
}
}