mirror of
https://github.com/frappe/books.git
synced 2024-11-14 09:24:04 +00:00
incr: create typed frappe
This commit is contained in:
parent
2afec6a74b
commit
72b6478e29
@ -1,3 +1,5 @@
|
|||||||
|
import { Frappe } from 'frappe/core/frappe';
|
||||||
|
|
||||||
interface AuthConfig {
|
interface AuthConfig {
|
||||||
serverURL: string;
|
serverURL: string;
|
||||||
backend: string;
|
backend: string;
|
||||||
@ -9,11 +11,13 @@ interface Session {
|
|||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Auth {
|
export class AuthHandler {
|
||||||
#config: AuthConfig;
|
#config: AuthConfig;
|
||||||
#session: Session;
|
#session: Session;
|
||||||
|
frappe: Frappe;
|
||||||
|
|
||||||
constructor() {
|
constructor(frappe: Frappe) {
|
||||||
|
this.frappe = frappe;
|
||||||
this.#config = {
|
this.#config = {
|
||||||
serverURL: '',
|
serverURL: '',
|
||||||
backend: 'sqlite',
|
backend: 'sqlite',
|
||||||
@ -34,6 +38,7 @@ export class Auth {
|
|||||||
return { ...this.#config };
|
return { ...this.#config };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init() {}
|
||||||
async login(email: string, password: string) {
|
async login(email: string, password: string) {
|
||||||
if (email === 'Administrator') {
|
if (email === 'Administrator') {
|
||||||
this.#session.user = 'Administrator';
|
this.#session.user = 'Administrator';
|
20
frappe/core/dbHandler.ts
Normal file
20
frappe/core/dbHandler.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { Frappe } from 'frappe/core/frappe';
|
||||||
|
|
||||||
|
type SingleValue = { fieldname: string; parent: string; value: unknown };
|
||||||
|
|
||||||
|
export class DbHandler {
|
||||||
|
frappe: Frappe;
|
||||||
|
constructor(frappe: Frappe) {
|
||||||
|
this.frappe = frappe;
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {}
|
||||||
|
close() {}
|
||||||
|
exists(doctype: string, name: string): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSingleValues(...fieldnames: Omit<SingleValue, 'value'>[]): SingleValue[] {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
254
frappe/core/docHandler.ts
Normal file
254
frappe/core/docHandler.ts
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
import { Field, Model } from '@/types/model';
|
||||||
|
import Doc from 'frappe/model/document';
|
||||||
|
import Meta from 'frappe/model/meta';
|
||||||
|
import { getDuplicates, getRandomString } from 'frappe/utils';
|
||||||
|
import Observable from 'frappe/utils/observable';
|
||||||
|
import { Frappe } from './frappe';
|
||||||
|
|
||||||
|
type DocMap = Record<string, Doc | undefined>;
|
||||||
|
type MetaMap = Record<string, Meta | undefined>;
|
||||||
|
interface DocData {
|
||||||
|
doctype: string;
|
||||||
|
name?: string;
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DocHandler {
|
||||||
|
frappe: Frappe;
|
||||||
|
singles: DocMap = {};
|
||||||
|
metaCache: MetaMap = {};
|
||||||
|
docs?: Observable<Doc>;
|
||||||
|
models: Record<string, Model | undefined> = {};
|
||||||
|
|
||||||
|
constructor(frappe: Frappe) {
|
||||||
|
this.frappe = frappe;
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.models = {};
|
||||||
|
this.metaCache = {};
|
||||||
|
this.docs = new Observable();
|
||||||
|
}
|
||||||
|
|
||||||
|
registerModels(models: Record<string, Model>) {
|
||||||
|
for (const doctype in models) {
|
||||||
|
const 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}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldnames = (metaDefinition.fields || [])
|
||||||
|
.map((df) => df.fieldname)
|
||||||
|
.sort();
|
||||||
|
|
||||||
|
const duplicateFieldnames = getDuplicates(fieldnames);
|
||||||
|
if (duplicateFieldnames.length > 0) {
|
||||||
|
throw new Error(
|
||||||
|
`Duplicate fields in ${doctype}: ${duplicateFieldnames.join(', ')}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.models[doctype] = metaDefinition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getModels(filterFunction: (name: Model) => boolean) {
|
||||||
|
const models: Model[] = [];
|
||||||
|
for (const doctype in this.models) {
|
||||||
|
models.push(this.models[doctype]!);
|
||||||
|
}
|
||||||
|
return filterFunction ? models.filter(filterFunction) : models;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache operations
|
||||||
|
*/
|
||||||
|
|
||||||
|
addToCache(doc: Doc) {
|
||||||
|
if (!this.docs) return;
|
||||||
|
|
||||||
|
// add to `docs` cache
|
||||||
|
const name = doc.name as string | undefined;
|
||||||
|
const doctype = doc.doctype as string | undefined;
|
||||||
|
|
||||||
|
if (!doctype || !name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.docs[doctype]) {
|
||||||
|
this.docs[doctype] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
(this.docs[doctype] as DocMap)[name] = doc;
|
||||||
|
|
||||||
|
// singles available as first level objects too
|
||||||
|
if (doctype === doc.name) {
|
||||||
|
this.singles[name] = doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// propogate change to `docs`
|
||||||
|
doc.on('change', (params: unknown) => {
|
||||||
|
this.docs!.trigger('change', params);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
removeFromCache(doctype: string, name: string) {
|
||||||
|
const docMap = this.docs?.[doctype] as DocMap | undefined;
|
||||||
|
const doc = docMap?.[name];
|
||||||
|
|
||||||
|
if (doc) {
|
||||||
|
delete docMap[name];
|
||||||
|
} else {
|
||||||
|
console.warn(`Document ${doctype} ${name} does not exist`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getDocFromCache(doctype: string, name: string): Doc | undefined {
|
||||||
|
const doc = (this.docs?.[doctype] as DocMap)?.[name];
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
isDirty(doctype: string, name: string) {
|
||||||
|
const doc = (this.docs?.[doctype] as DocMap)?.[name];
|
||||||
|
if (doc === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!doc._dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Meta Operations
|
||||||
|
*/
|
||||||
|
|
||||||
|
getMeta(doctype: string): Meta {
|
||||||
|
const meta = this.metaCache[doctype];
|
||||||
|
if (meta) {
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
const model = this.models?.[doctype];
|
||||||
|
if (!model) {
|
||||||
|
throw new Error(`${doctype} is not a registered doctype`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.metaCache[doctype] = new this.frappe.Meta!(model);
|
||||||
|
return this.metaCache[doctype]!;
|
||||||
|
}
|
||||||
|
|
||||||
|
createMeta(fields: Field[]) {
|
||||||
|
return new this.frappe.Meta!({ isCustom: 1, fields });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Doc Operations
|
||||||
|
*/
|
||||||
|
|
||||||
|
async getDoc(
|
||||||
|
doctype: string,
|
||||||
|
name: string,
|
||||||
|
options = { skipDocumentCache: false }
|
||||||
|
) {
|
||||||
|
let doc = null;
|
||||||
|
if (!options?.skipDocumentCache) {
|
||||||
|
doc = this.getDocFromCache(doctype, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doc) {
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DocClass = this.getDocumentClass(doctype);
|
||||||
|
doc = new DocClass({
|
||||||
|
doctype: doctype,
|
||||||
|
name: name,
|
||||||
|
});
|
||||||
|
|
||||||
|
await doc.load();
|
||||||
|
this.addToCache(doc);
|
||||||
|
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDocumentClass(doctype: string): typeof Doc {
|
||||||
|
const meta = this.getMeta(doctype);
|
||||||
|
let documentClass = this.frappe.Document!;
|
||||||
|
if (meta && meta.documentClass) {
|
||||||
|
documentClass = meta.documentClass as typeof Doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return documentClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSingle(doctype: string) {
|
||||||
|
return await this.getDoc(doctype, doctype);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDuplicate(doc: Doc) {
|
||||||
|
const doctype = doc.doctype as string;
|
||||||
|
const newDoc = await this.getEmptyDoc(doctype);
|
||||||
|
const meta = this.getMeta(doctype);
|
||||||
|
|
||||||
|
const fields = meta.getValidFields() as Field[];
|
||||||
|
|
||||||
|
for (const field of fields) {
|
||||||
|
if (['name', 'submitted'].includes(field.fieldname)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
newDoc[field.fieldname] = doc[field.fieldname];
|
||||||
|
if (field.fieldtype === 'Table') {
|
||||||
|
const value = (doc[field.fieldname] as DocData[]) || [];
|
||||||
|
newDoc[field.fieldname] = value.map((d) => {
|
||||||
|
const childData = Object.assign({}, d);
|
||||||
|
childData.name = '';
|
||||||
|
return childData;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newDoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEmptyDoc(doctype: string, cacheDoc: boolean = true): Doc {
|
||||||
|
const doc = this.getNewDoc({ doctype });
|
||||||
|
doc._notInserted = true;
|
||||||
|
doc.name = getRandomString();
|
||||||
|
|
||||||
|
if (cacheDoc) {
|
||||||
|
this.addToCache(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
getNewDoc(data: DocData): Doc {
|
||||||
|
const DocClass = this.getDocumentClass(data.doctype);
|
||||||
|
const doc = new DocClass(data);
|
||||||
|
doc.setDefaults();
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
async syncDoc(data: DocData) {
|
||||||
|
let doc;
|
||||||
|
const { doctype, name } = data;
|
||||||
|
if (!doctype || !name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const docExists = await this.frappe.db.exists(doctype, name);
|
||||||
|
if (docExists) {
|
||||||
|
doc = await this.getDoc(doctype, name);
|
||||||
|
Object.assign(doc, data);
|
||||||
|
await doc.update();
|
||||||
|
} else {
|
||||||
|
doc = this.getNewDoc(data);
|
||||||
|
await doc.insert();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
144
frappe/core/frappe.ts
Normal file
144
frappe/core/frappe.ts
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
import { ErrorLog } from '@/errorHandling';
|
||||||
|
import { Model } from '@/types/model';
|
||||||
|
import Doc from 'frappe/model/document';
|
||||||
|
import Meta from 'frappe/model/meta';
|
||||||
|
import { getMoneyMaker, MoneyMaker } from 'pesa';
|
||||||
|
import { markRaw } from 'vue';
|
||||||
|
import {
|
||||||
|
DEFAULT_DISPLAY_PRECISION,
|
||||||
|
DEFAULT_INTERNAL_PRECISION,
|
||||||
|
} from '../utils/consts';
|
||||||
|
import * as errors from '../utils/errors';
|
||||||
|
import { format } from '../utils/format';
|
||||||
|
import { t, T } from '../utils/translation';
|
||||||
|
import { AuthHandler } from './authHandler';
|
||||||
|
import { DbHandler } from './dbHandler';
|
||||||
|
import { DocHandler } from './docHandler';
|
||||||
|
|
||||||
|
export class Frappe {
|
||||||
|
t = t;
|
||||||
|
T = T;
|
||||||
|
format = format;
|
||||||
|
|
||||||
|
errors = errors;
|
||||||
|
isElectron = false;
|
||||||
|
isServer = false;
|
||||||
|
|
||||||
|
pesa: MoneyMaker;
|
||||||
|
|
||||||
|
auth: AuthHandler;
|
||||||
|
doc: DocHandler;
|
||||||
|
db: DbHandler;
|
||||||
|
|
||||||
|
Meta?: typeof Meta;
|
||||||
|
Document?: typeof Doc;
|
||||||
|
|
||||||
|
_initialized: boolean = false;
|
||||||
|
|
||||||
|
errorLog?: ErrorLog[];
|
||||||
|
methods?: Record<string, Function>;
|
||||||
|
temp?: Record<string, unknown>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.auth = new AuthHandler(this);
|
||||||
|
this.doc = new DocHandler(this);
|
||||||
|
this.db = new DbHandler(this);
|
||||||
|
this.pesa = getMoneyMaker({
|
||||||
|
currency: 'XXX',
|
||||||
|
precision: DEFAULT_INTERNAL_PRECISION,
|
||||||
|
display: DEFAULT_DISPLAY_PRECISION,
|
||||||
|
wrapper: markRaw,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get initialized() {
|
||||||
|
return this._initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
get docs() {
|
||||||
|
return this.doc.docs;
|
||||||
|
}
|
||||||
|
|
||||||
|
get models() {
|
||||||
|
return this.doc.models;
|
||||||
|
}
|
||||||
|
|
||||||
|
async initializeAndRegister(customModels = {}, force = false) {
|
||||||
|
this.init(force);
|
||||||
|
|
||||||
|
this.Meta = (await import('frappe/model/meta')).default;
|
||||||
|
this.Document = (await import('frappe/model/document')).default;
|
||||||
|
|
||||||
|
const coreModels = await import('frappe/models');
|
||||||
|
this.doc.registerModels(coreModels.default as Record<string, Model>);
|
||||||
|
this.doc.registerModels(customModels);
|
||||||
|
}
|
||||||
|
|
||||||
|
init(force: boolean) {
|
||||||
|
if (this._initialized && !force) return;
|
||||||
|
|
||||||
|
this.methods = {};
|
||||||
|
this.errorLog = [];
|
||||||
|
|
||||||
|
// temp params while calling routes
|
||||||
|
this.temp = {};
|
||||||
|
|
||||||
|
this.doc.init();
|
||||||
|
this.auth.init();
|
||||||
|
this.db.init();
|
||||||
|
this._initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async initializeMoneyMaker(currency: string = 'XXX') {
|
||||||
|
// to be called after db initialization
|
||||||
|
const values =
|
||||||
|
(await this.db?.getSingleValues(
|
||||||
|
{
|
||||||
|
fieldname: 'internalPrecision',
|
||||||
|
parent: 'SystemSettings',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: 'displayPrecision',
|
||||||
|
parent: 'SystemSettings',
|
||||||
|
}
|
||||||
|
)) ?? [];
|
||||||
|
|
||||||
|
const acc = values.reduce((acc, sv) => {
|
||||||
|
acc[sv.fieldname] = sv.value as string | number | undefined;
|
||||||
|
return acc;
|
||||||
|
}, {} as Record<string, string | number | undefined>);
|
||||||
|
|
||||||
|
let precision: string | number =
|
||||||
|
acc.internalPrecision ?? DEFAULT_INTERNAL_PRECISION;
|
||||||
|
let display: string | number =
|
||||||
|
acc.displayPrecision ?? DEFAULT_DISPLAY_PRECISION;
|
||||||
|
|
||||||
|
if (typeof precision === 'string') {
|
||||||
|
precision = parseInt(precision);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof display === 'string') {
|
||||||
|
display = parseInt(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pesa = getMoneyMaker({
|
||||||
|
currency,
|
||||||
|
precision,
|
||||||
|
display,
|
||||||
|
wrapper: markRaw,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.db.close();
|
||||||
|
this.auth.logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
store = {
|
||||||
|
isDevelopment: false,
|
||||||
|
appVersion: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export { T, t };
|
||||||
|
export default new Frappe();
|
@ -1,6 +1,6 @@
|
|||||||
import { getMoneyMaker } from 'pesa';
|
import { getMoneyMaker } from 'pesa';
|
||||||
import { markRaw } from 'vue';
|
import { markRaw } from 'vue';
|
||||||
import { Auth } from './core/auth';
|
import { AuthHandler } from './core/authHandler';
|
||||||
import { asyncHandler, getDuplicates, getRandomString } from './utils';
|
import { asyncHandler, getDuplicates, getRandomString } from './utils';
|
||||||
import {
|
import {
|
||||||
DEFAULT_DISPLAY_PRECISION,
|
DEFAULT_DISPLAY_PRECISION,
|
||||||
@ -11,7 +11,7 @@ import { format } from './utils/format';
|
|||||||
import Observable from './utils/observable';
|
import Observable from './utils/observable';
|
||||||
import { t, T } from './utils/translation';
|
import { t, T } from './utils/translation';
|
||||||
|
|
||||||
class Frappe {
|
export class Frappe {
|
||||||
t = t;
|
t = t;
|
||||||
T = T;
|
T = T;
|
||||||
format = format;
|
format = format;
|
||||||
@ -21,7 +21,7 @@ class Frappe {
|
|||||||
isServer = false;
|
isServer = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.auth = new Auth();
|
this.auth = new AuthHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
async initializeAndRegister(customModels = {}, force = false) {
|
async initializeAndRegister(customModels = {}, force = false) {
|
||||||
|
@ -3,7 +3,7 @@ enum EventType {
|
|||||||
OnceListeners = '_onceListeners',
|
OnceListeners = '_onceListeners',
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Observable {
|
export default class Observable<T> {
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
_isHot: Map<string, boolean>;
|
_isHot: Map<string, boolean>;
|
||||||
_eventQueue: Map<string, unknown[]>;
|
_eventQueue: Map<string, unknown[]>;
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"luxon": "^2.0.2",
|
"luxon": "^2.0.2",
|
||||||
"node-fetch": "2",
|
"node-fetch": "2",
|
||||||
"pesa": "^1.1.3",
|
"pesa": "^1.1.11",
|
||||||
"sqlite3": "npm:@vscode/sqlite3@^5.0.7",
|
"sqlite3": "npm:@vscode/sqlite3@^5.0.7",
|
||||||
"vue": "^3.2.30",
|
"vue": "^3.2.30",
|
||||||
"vue-router": "^4.0.12"
|
"vue-router": "^4.0.12"
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import frappe, { t } from 'frappe';
|
import frappe, { t } from 'frappe';
|
||||||
|
import Document from 'frappe/model/document';
|
||||||
import {
|
import {
|
||||||
DuplicateEntryError,
|
DuplicateEntryError,
|
||||||
LinkValidationError,
|
LinkValidationError,
|
||||||
MandatoryError,
|
MandatoryError,
|
||||||
ValidationError,
|
ValidationError,
|
||||||
} from 'frappe/utils/errors';
|
} from 'frappe/utils/errors';
|
||||||
import Document from 'frappe/model/document';
|
|
||||||
import config, { ConfigKeys, TelemetrySetting } from './config';
|
import config, { ConfigKeys, TelemetrySetting } from './config';
|
||||||
import { IPC_ACTIONS, IPC_MESSAGES } from './messages';
|
import { IPC_ACTIONS, IPC_MESSAGES } from './messages';
|
||||||
import telemetry from './telemetry/telemetry';
|
import telemetry from './telemetry/telemetry';
|
||||||
import { showMessageDialog, showToast } from './utils';
|
import { showMessageDialog, showToast } from './utils';
|
||||||
|
|
||||||
interface ErrorLog {
|
export interface ErrorLog {
|
||||||
name: string;
|
name: string;
|
||||||
message: string;
|
message: string;
|
||||||
stack?: string;
|
stack?: string;
|
||||||
|
@ -1,20 +1,3 @@
|
|||||||
import FormControl from '@/components/Controls/FormControl';
|
|
||||||
import LanguageSelector from '@/components/Controls/LanguageSelector.vue';
|
|
||||||
import Popover from '@/components/Popover';
|
|
||||||
import TwoColumnForm from '@/components/TwoColumnForm';
|
|
||||||
import config from '@/config';
|
|
||||||
import { connectToLocalDatabase, purgeCache } from '@/initialization';
|
|
||||||
import { IPC_MESSAGES } from '@/messages';
|
|
||||||
import { setLanguageMap, showMessageDialog } from '@/utils';
|
|
||||||
import { ipcRenderer } from 'electron';
|
|
||||||
import frappe from 'frappe';
|
|
||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
|
||||||
import {
|
|
||||||
getErrorMessage, handleErrorWithDialog, showErrorDialog
|
|
||||||
} from '../../errorHandling';
|
|
||||||
import setupCompany from './setupCompany';
|
|
||||||
import Slide from './Slide.vue';
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Slide
|
<Slide
|
||||||
|
@ -30,6 +30,7 @@ export interface Field {
|
|||||||
readOnly?: number;
|
readOnly?: number;
|
||||||
hidden?: number | Function;
|
hidden?: number | Function;
|
||||||
options?: string[];
|
options?: string[];
|
||||||
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Doc {
|
export interface Doc {
|
||||||
@ -38,3 +39,15 @@ export interface Doc {
|
|||||||
insert: () => Promise<void>;
|
insert: () => Promise<void>;
|
||||||
submit: () => Promise<void>;
|
submit: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Model {
|
||||||
|
label?: string;
|
||||||
|
name: string;
|
||||||
|
doctype?: string;
|
||||||
|
fields: Field[];
|
||||||
|
isSingle?: number; // boolean
|
||||||
|
regional?: number; // boolean
|
||||||
|
augmented?: number; // boolean
|
||||||
|
keywordFields?: string[];
|
||||||
|
quickEditFields?: string[];
|
||||||
|
}
|
@ -8573,10 +8573,10 @@ performance-now@^2.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||||
|
|
||||||
pesa@^1.1.3:
|
pesa@^1.1.11:
|
||||||
version "1.1.6"
|
version "1.1.11"
|
||||||
resolved "https://registry.yarnpkg.com/pesa/-/pesa-1.1.6.tgz#a123dc7a31c977bbfb6dbbee14a9913c8385cd0f"
|
resolved "https://registry.yarnpkg.com/pesa/-/pesa-1.1.11.tgz#85829e5aa11ea3e44d7c9ea4b500e4d6def6dc5f"
|
||||||
integrity sha512-EDs4Tj8QX7hZITkKhfHeVT/9oQRqRPyF3pVet1FqGX94/zkcqreBYb94ojUVrzZmir26+IHaKyCpTB6eqC04uw==
|
integrity sha512-eyl0lpdUIV0dNXVeTMnhBJj6u9GRIYwP+vFdUN+767Fv3PNQHPHAkCQJqDseGfEF75lhe23ZnfbA/uMidlq5/Q==
|
||||||
|
|
||||||
pg-connection-string@2.5.0:
|
pg-connection-string@2.5.0:
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user