mirror of
https://github.com/frappe/books.git
synced 2024-11-09 15:20:56 +00:00
incr: update DatabaseSelector
This commit is contained in:
parent
b6163b4cac
commit
6accde0e17
@ -2,5 +2,5 @@ import { Patch } from '../database/types';
|
||||
import testPatch from './testPatch';
|
||||
|
||||
export default [
|
||||
{ name: 'testPatch', version: '0.4.2-beta.0', patch: testPatch },
|
||||
{ name: 'testPatch', version: '0.5.0-beta.0', patch: testPatch },
|
||||
] as Patch[];
|
||||
|
@ -97,6 +97,8 @@ export class AuthHandler {
|
||||
// TODO: Implement this with auth flow
|
||||
}
|
||||
|
||||
purgeCache() {}
|
||||
|
||||
#getServerURL() {
|
||||
return this.#config.serverURL || '';
|
||||
}
|
||||
|
@ -37,6 +37,10 @@ export class DatabaseHandler extends DatabaseBase {
|
||||
}
|
||||
}
|
||||
|
||||
get isConnected() {
|
||||
return !!this.dbPath;
|
||||
}
|
||||
|
||||
async createNewDatabase(dbPath: string, countryCode: string) {
|
||||
countryCode = await this.#demux.createNewDatabase(dbPath, countryCode);
|
||||
await this.init();
|
||||
@ -60,6 +64,12 @@ export class DatabaseHandler extends DatabaseBase {
|
||||
}
|
||||
}
|
||||
|
||||
purgeCache() {
|
||||
this.dbPath = undefined;
|
||||
this.schemaMap = {};
|
||||
this.fieldValueMap = {};
|
||||
}
|
||||
|
||||
async insert(
|
||||
schemaName: string,
|
||||
docValueMap: DocValueMap
|
||||
@ -162,6 +172,7 @@ export class DatabaseHandler extends DatabaseBase {
|
||||
// Other
|
||||
async close(): Promise<void> {
|
||||
await this.#demux.call('close');
|
||||
this.purgeCache();
|
||||
}
|
||||
|
||||
async exists(schemaName: string, name?: string): Promise<boolean> {
|
||||
|
@ -2,6 +2,7 @@ import Doc from 'fyo/model/doc';
|
||||
import { DocMap, ModelMap, SinglesMap } from 'fyo/model/types';
|
||||
import { coreModels } from 'fyo/models';
|
||||
import Observable from 'fyo/utils/observable';
|
||||
import { Schema } from 'schemas/types';
|
||||
import { getRandomString } from 'utils';
|
||||
import { Fyo } from '..';
|
||||
import { DocValue, DocValueMap } from './types';
|
||||
@ -18,9 +19,14 @@ export class DocHandler {
|
||||
|
||||
init() {
|
||||
this.models = {};
|
||||
this.singles = {};
|
||||
this.docs = new Observable();
|
||||
}
|
||||
|
||||
purgeCache() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
registerModels(models: ModelMap, regionalModels: ModelMap = {}) {
|
||||
for (const schemaName in this.fyo.db.schemaMap) {
|
||||
if (coreModels[schemaName] !== undefined) {
|
||||
@ -157,9 +163,14 @@ export class DocHandler {
|
||||
return doc;
|
||||
}
|
||||
|
||||
getNewDoc(schemaName: string, data: DocValueMap = {}): Doc {
|
||||
const Model = this.getModel(schemaName);
|
||||
const schema = this.fyo.schemaMap[schemaName];
|
||||
getNewDoc(
|
||||
schemaName: string,
|
||||
data: DocValueMap = {},
|
||||
schema?: Schema,
|
||||
Model?: typeof Doc
|
||||
): Doc {
|
||||
Model ??= this.getModel(schemaName);
|
||||
schema ??= this.fyo.schemaMap[schemaName];
|
||||
if (schema === undefined) {
|
||||
throw new Error(`Schema not found for ${schemaName}`);
|
||||
}
|
||||
|
25
fyo/index.ts
25
fyo/index.ts
@ -34,8 +34,7 @@ export class Fyo {
|
||||
|
||||
_initialized: boolean = false;
|
||||
|
||||
errorLog?: ErrorLog[];
|
||||
methods?: Record<string, Function>;
|
||||
errorLog: ErrorLog[] = [];
|
||||
temp?: Record<string, unknown>;
|
||||
|
||||
currencyFormatter?: Intl.NumberFormat;
|
||||
@ -113,9 +112,6 @@ export class Fyo {
|
||||
}
|
||||
|
||||
async #initializeModules() {
|
||||
this.methods = {};
|
||||
this.errorLog = [];
|
||||
|
||||
// temp params while calling routes
|
||||
this.temp = {};
|
||||
|
||||
@ -170,6 +166,25 @@ export class Fyo {
|
||||
return schema?.fields.find((f) => f.fieldname === fieldname);
|
||||
}
|
||||
|
||||
purgeCache() {
|
||||
this.pesa = getMoneyMaker({
|
||||
currency: DEFAULT_CURRENCY,
|
||||
precision: DEFAULT_INTERNAL_PRECISION,
|
||||
display: DEFAULT_DISPLAY_PRECISION,
|
||||
wrapper: markRaw,
|
||||
});
|
||||
|
||||
this._initialized = false;
|
||||
this.temp = {};
|
||||
this.currencyFormatter = undefined;
|
||||
this.currencySymbols = {};
|
||||
this.errorLog = [];
|
||||
this.temp = {};
|
||||
this.db.purgeCache();
|
||||
this.auth.purgeCache();
|
||||
this.doc.purgeCache();
|
||||
}
|
||||
|
||||
store = {
|
||||
isDevelopment: false,
|
||||
appVersion: '',
|
||||
|
@ -37,6 +37,7 @@ import {
|
||||
HiddenMap,
|
||||
ListsMap,
|
||||
ListViewSettings,
|
||||
ReadOnlyMap,
|
||||
RequiredMap,
|
||||
TreeViewSettings,
|
||||
ValidationMap,
|
||||
@ -96,6 +97,11 @@ export default class Doc extends Observable<DocValue | Doc[]> {
|
||||
return this._dirty;
|
||||
}
|
||||
|
||||
get quickEditFields() {
|
||||
const fieldnames = this.schema.quickEditFields ?? ['name'];
|
||||
return fieldnames.map((f) => this.fieldMap[f]);
|
||||
}
|
||||
|
||||
_setInitialValues(data: DocValueMap) {
|
||||
for (const fieldname in data) {
|
||||
const value = data[fieldname];
|
||||
@ -393,8 +399,13 @@ export default class Doc extends Observable<DocValue | Doc[]> {
|
||||
);
|
||||
}
|
||||
|
||||
getLink(fieldname: string) {
|
||||
return this._links ? this._links[fieldname] : null;
|
||||
getLink(fieldname: string): Doc | null {
|
||||
const link = this._links?.[fieldname];
|
||||
if (link === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
syncValues(data: DocValueMap) {
|
||||
@ -736,6 +747,7 @@ export default class Doc extends Observable<DocValue | Doc[]> {
|
||||
validations: ValidationMap = {};
|
||||
required: RequiredMap = {};
|
||||
hidden: HiddenMap = {};
|
||||
readOnly: ReadOnlyMap = {};
|
||||
dependsOn: DependsOnMap = {};
|
||||
getCurrencies: CurrenciesMap = {};
|
||||
|
||||
|
@ -21,9 +21,10 @@ import Doc from './doc';
|
||||
export type FormulaReturn = DocValue | DocValueMap[] | undefined | Doc[];
|
||||
export type Formula = () => Promise<FormulaReturn> | FormulaReturn;
|
||||
export type Default = () => DocValue;
|
||||
export type Validation = (value: DocValue) => Promise<void>;
|
||||
export type Validation = (value: DocValue) => Promise<void> | void;
|
||||
export type Required = () => boolean;
|
||||
export type Hidden = () => boolean;
|
||||
export type ReadOnly = () => boolean;
|
||||
export type GetCurrency = () => string;
|
||||
|
||||
export type FormulaMap = Record<string, Formula | undefined>;
|
||||
@ -32,6 +33,7 @@ export type ValidationMap = Record<string, Validation | undefined>;
|
||||
export type RequiredMap = Record<string, Required | undefined>;
|
||||
export type CurrenciesMap = Record<string, GetCurrency>;
|
||||
export type HiddenMap = Record<string, Hidden>;
|
||||
export type ReadOnlyMap = Record<string, ReadOnly>;
|
||||
export type DependsOnMap = Record<string, string[]>;
|
||||
|
||||
/**
|
||||
|
@ -1,16 +1,17 @@
|
||||
import { DocValue } from 'fyo/core/types';
|
||||
import { ValidationError, ValueError } from 'fyo/utils/errors';
|
||||
import { t } from 'fyo/utils/translation';
|
||||
import { OptionField } from 'schemas/types';
|
||||
|
||||
export function email(value: string) {
|
||||
const isValid = /(.+)@(.+){2,}\.(.+){2,}/.test(value);
|
||||
export function validateEmail(value: DocValue) {
|
||||
const isValid = /(.+)@(.+){2,}\.(.+){2,}/.test(value as string);
|
||||
if (!isValid) {
|
||||
throw new ValidationError(`Invalid email: ${value}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function phone(value: string) {
|
||||
const isValid = /[+]{0,1}[\d ]+/.test(value);
|
||||
export function validatePhoneNumber(value: DocValue) {
|
||||
const isValid = /[+]{0,1}[\d ]+/.test(value as string);
|
||||
if (!isValid) {
|
||||
throw new ValidationError(`Invalid phone: ${value}`);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { Fyo } from 'fyo';
|
||||
import Doc from 'fyo/model/doc';
|
||||
import { Action } from 'fyo/model/types';
|
||||
import { pesa } from 'pesa';
|
||||
@ -51,3 +52,24 @@ export function getActions(doc: Doc): Action[] {
|
||||
|
||||
return Model.getActions(doc.fyo);
|
||||
}
|
||||
|
||||
export async function getSingleValue(
|
||||
fieldname: string,
|
||||
parent: string,
|
||||
fyo: Fyo
|
||||
) {
|
||||
if (!fyo.db.isConnected) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const res = await fyo.db.getSingleValues({ fieldname, parent });
|
||||
const singleValue = res.find(
|
||||
(f) => f.fieldname === fieldname && f.parent === parent
|
||||
);
|
||||
|
||||
if (singleValue === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return singleValue.value;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Doc from 'fyo/model/doc';
|
||||
import { FiltersMap, ListsMap } from 'fyo/model/types';
|
||||
import { FiltersMap, ListsMap, ValidationMap } from 'fyo/model/types';
|
||||
import { validateEmail } from 'fyo/model/validationFunction';
|
||||
import countryInfo from '../../../fixtures/countryInfo.json';
|
||||
|
||||
export class AccountingSettings extends Doc {
|
||||
@ -14,6 +15,10 @@ export class AccountingSettings extends Doc {
|
||||
}),
|
||||
};
|
||||
|
||||
validations: ValidationMap = {
|
||||
email: validateEmail,
|
||||
};
|
||||
|
||||
static lists: ListsMap = {
|
||||
country: () => Object.keys(countryInfo),
|
||||
};
|
||||
|
@ -5,7 +5,12 @@ import {
|
||||
FiltersMap,
|
||||
FormulaMap,
|
||||
ListViewSettings,
|
||||
ValidationMap,
|
||||
} from 'fyo/model/types';
|
||||
import {
|
||||
validateEmail,
|
||||
validatePhoneNumber,
|
||||
} from 'fyo/model/validationFunction';
|
||||
import { PartyRole } from './types';
|
||||
|
||||
export class Party extends Doc {
|
||||
@ -71,6 +76,11 @@ export class Party extends Doc {
|
||||
},
|
||||
};
|
||||
|
||||
validations: ValidationMap = {
|
||||
email: validateEmail,
|
||||
phone: validatePhoneNumber,
|
||||
};
|
||||
|
||||
static filters: FiltersMap = {
|
||||
defaultAccount: (doc: Doc) => {
|
||||
const role = doc.role as PartyRole;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { t } from 'fyo';
|
||||
import Doc from 'fyo/model/doc';
|
||||
import { FormulaMap, ListsMap } from 'fyo/model/types';
|
||||
import { FormulaMap, ListsMap, ValidationMap } from 'fyo/model/types';
|
||||
import { validateEmail } from 'fyo/model/validationFunction';
|
||||
import { DateTime } from 'luxon';
|
||||
import countryInfo from '../../../fixtures/countryInfo.json';
|
||||
|
||||
@ -88,6 +89,10 @@ export class SetupWizard extends Doc {
|
||||
},
|
||||
};
|
||||
|
||||
validations: ValidationMap = {
|
||||
email: validateEmail,
|
||||
};
|
||||
|
||||
static lists: ListsMap = {
|
||||
country: () => Object.keys(countryInfo),
|
||||
chartOfAccounts: () => getCOAList().map(({ name }) => name),
|
||||
|
@ -40,6 +40,7 @@ function removeFields(schemaMap: SchemaMap): SchemaMap {
|
||||
schema.keywordFields = schema.keywordFields?.filter(
|
||||
(fn) => fn !== fieldname
|
||||
);
|
||||
|
||||
if (schema.inlineEditDisplayField === fieldname) {
|
||||
delete schema.inlineEditDisplayField;
|
||||
}
|
||||
|
68
src/App.vue
68
src/App.vue
@ -3,7 +3,8 @@
|
||||
id="app"
|
||||
class="h-screen flex flex-col font-sans overflow-hidden antialiased"
|
||||
>
|
||||
<!-- <WindowsTitleBar v-if="platform === 'Windows'" />
|
||||
<WindowsTitleBar v-if="platform === 'Windows'" />
|
||||
<!--
|
||||
<Desk
|
||||
class="flex-1"
|
||||
v-if="activeScreen === 'Desk'"
|
||||
@ -11,9 +12,9 @@
|
||||
/>-->
|
||||
<DatabaseSelector
|
||||
v-if="activeScreen === 'DatabaseSelector'"
|
||||
@database-connect="showSetupWizardOrDesk(true)"
|
||||
@file-selected="fileSelected"
|
||||
/>
|
||||
<!--<SetupWizard
|
||||
<SetupWizard
|
||||
v-if="activeScreen === 'SetupWizard'"
|
||||
@setup-complete="setupComplete"
|
||||
@setup-canceled="setupCanceled"
|
||||
@ -25,18 +26,20 @@
|
||||
>
|
||||
<div id="toast-target" />
|
||||
</div>
|
||||
<!-- TODO: check this and uncomment
|
||||
<TelemetryModal />-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import fs from 'fs/promises';
|
||||
import { fyo } from './initFyo';
|
||||
import DatabaseSelector from './pages/DatabaseSelector';
|
||||
// import Desk from './pages/Desk';
|
||||
// import SetupWizard from './pages/SetupWizard/SetupWizard';
|
||||
import WindowsTitleBar from './components/WindowsTitleBar.vue';
|
||||
import { fyo, initializeInstance } from './initFyo';
|
||||
import DatabaseSelector from './pages/DatabaseSelector.vue';
|
||||
import SetupWizard from './pages/SetupWizard/SetupWizard.vue';
|
||||
import './styles/index.css';
|
||||
import { checkForUpdates, routeTo } from './utils';
|
||||
import { checkForUpdates } from './utils/ipcCalls';
|
||||
import { routeTo } from './utils/ui';
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
@ -47,9 +50,9 @@ export default {
|
||||
},
|
||||
components: {
|
||||
// Desk,
|
||||
// SetupWizard,
|
||||
SetupWizard,
|
||||
DatabaseSelector,
|
||||
// WindowsTitleBar,
|
||||
WindowsTitleBar,
|
||||
// TelemetryModal,
|
||||
},
|
||||
async mounted() {
|
||||
@ -73,29 +76,45 @@ export default {
|
||||
}
|
||||
*/
|
||||
|
||||
this.activeScreen = 'DatabaseSelector';
|
||||
// this.activeScreen = 'DatabaseSelector';
|
||||
this.activeScreen = 'SetupWizard';
|
||||
},
|
||||
methods: {
|
||||
async setupComplete() {
|
||||
// TODO: Complete this
|
||||
// await postSetup();
|
||||
await this.showSetupWizardOrDesk(true);
|
||||
// await this.showSetupWizardOrDesk(true);
|
||||
},
|
||||
async showSetupWizardOrDesk(resetRoute = false) {
|
||||
const { setupComplete } = fyo.singles.AccountingSettings;
|
||||
if (!setupComplete) {
|
||||
async fileSelected(filePath, isNew) {
|
||||
console.log('from App.vue', filePath, isNew);
|
||||
if (isNew) {
|
||||
this.activeScreen = 'SetupWizard';
|
||||
} else {
|
||||
this.activeScreen = 'Desk';
|
||||
await checkForUpdates(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.showSetupWizardOrDesk(filePath);
|
||||
},
|
||||
async showSetupWizardOrDesk(filePath, resetRoute = false) {
|
||||
const countryCode = await fyo.db.connectToDatabase(filePath);
|
||||
const setupComplete = await getSetupComplete();
|
||||
|
||||
if (!setupComplete) {
|
||||
this.activeScreen = 'SetupWizard';
|
||||
return;
|
||||
}
|
||||
|
||||
await initializeInstance(filePath, false, countryCode);
|
||||
this.activeScreen = 'Desk';
|
||||
await checkForUpdates(false);
|
||||
if (!resetRoute) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { onboardingComplete } = await fyo.getSingle('GetStarted');
|
||||
const { hideGetStarted } = await fyo.getSingle('SystemSettings');
|
||||
await this.setDeskRoute();
|
||||
},
|
||||
async setDeskRoute() {
|
||||
const { onboardingComplete } = await fyo.doc.getSingle('GetStarted');
|
||||
const { hideGetStarted } = await fyo.doc.getSingle('SystemSettings');
|
||||
|
||||
if (hideGetStarted || onboardingComplete) {
|
||||
routeTo('/');
|
||||
@ -105,14 +124,15 @@ export default {
|
||||
},
|
||||
async changeDbFile() {
|
||||
fyo.config.set('lastSelectedFilePath', null);
|
||||
telemetry.stop();
|
||||
// TODO: purgeCache(true)
|
||||
// await purgeCache(true);
|
||||
fyo.telemetry.stop();
|
||||
fyo.purgeCache();
|
||||
this.activeScreen = 'DatabaseSelector';
|
||||
},
|
||||
async setupCanceled() {
|
||||
const filePath = fyo.config.get('lastSelectedFilePath');
|
||||
await fs.unlink(filePath);
|
||||
if (filePath) {
|
||||
await fs.unlink(filePath);
|
||||
}
|
||||
this.changeDbFile();
|
||||
},
|
||||
},
|
||||
|
13
src/README.md
Normal file
13
src/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# src
|
||||
|
||||
This is where all the frontend code lives
|
||||
|
||||
## Initialization
|
||||
New Instance
|
||||
1. Run _Setup Wizard_ for initialization values (eg: `countryCode`).
|
||||
2.
|
||||
|
||||
Existing Instance
|
||||
1. Connect to db
|
||||
2. Check if _Setup Wizard_ has been completed, if not, jump to **New Instance**
|
||||
3. Call `initFyo/initializeInstance` with `dbPath` and `countryCode`
|
@ -1,18 +1,19 @@
|
||||
import Data from './Data';
|
||||
import Select from './Select';
|
||||
import Link from './Link';
|
||||
import Date from './Date';
|
||||
import Table from './Table';
|
||||
<script>
|
||||
import { h } from 'vue';
|
||||
import AttachImage from './AttachImage';
|
||||
import AutoComplete from './AutoComplete';
|
||||
import Check from './Check';
|
||||
import AttachImage from './AttachImage';
|
||||
import DynamicLink from './DynamicLink';
|
||||
import Int from './Int';
|
||||
import Float from './Float';
|
||||
import Currency from './Currency';
|
||||
import Text from './Text';
|
||||
import Color from './Color';
|
||||
import { h } from 'vue';
|
||||
import Currency from './Currency';
|
||||
import Data from './Data';
|
||||
import Date from './Date';
|
||||
import DynamicLink from './DynamicLink';
|
||||
import Float from './Float';
|
||||
import Int from './Int';
|
||||
import Link from './Link';
|
||||
import Select from './Select';
|
||||
import Table from './Table';
|
||||
import Text from './Text';
|
||||
|
||||
export default {
|
||||
name: 'FormControl',
|
||||
@ -48,3 +49,4 @@ export default {
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
@ -9,9 +9,8 @@
|
||||
<script>
|
||||
import { DEFAULT_LANGUAGE } from 'fyo/utils/consts';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { setLanguageMap } from 'src/utils';
|
||||
import { languageCodeMap } from 'src/utils/language';
|
||||
import FormControl from './FormControl';
|
||||
import { languageCodeMap, setLanguageMap } from 'src/utils/language';
|
||||
import FormControl from './FormControl.vue';
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
@ -37,7 +36,7 @@ export default {
|
||||
return {
|
||||
fieldname: 'language',
|
||||
label: this.t`Language`,
|
||||
fieldtype: 'Select',
|
||||
fieldtype: 'AutoComplete',
|
||||
options: Object.keys(languageCodeMap),
|
||||
default: fyo.config.get('language') ?? DEFAULT_LANGUAGE,
|
||||
description: this.t`Set the display language.`,
|
||||
|
@ -119,7 +119,7 @@
|
||||
import { t } from 'fyo';
|
||||
import { getRandomString } from 'utils';
|
||||
import Button from './Button';
|
||||
import FormControl from './Controls/FormControl';
|
||||
import FormControl from './Controls/FormControl.vue';
|
||||
import Icon from './Icon';
|
||||
import Popover from './Popover';
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
<template>
|
||||
<div class="text-sm" :class="{ 'border-t': !noBorder }">
|
||||
<template v-for="df in formFields">
|
||||
<!-- Table Field Form (Eg: PaymentFor) -->
|
||||
<FormControl
|
||||
:key="df.fieldname"
|
||||
v-if="df.fieldtype === 'Table'"
|
||||
:key="`${df.fieldname}-table`"
|
||||
ref="controls"
|
||||
size="small"
|
||||
:df="df"
|
||||
@ -11,91 +12,93 @@
|
||||
:read-only="evaluateReadOnly(df)"
|
||||
@change="(value) => onChange(df, value)"
|
||||
/>
|
||||
<template v-else>
|
||||
<div class="border-b" :key="df.fieldname" v-if="renderInline(df)">
|
||||
<TwoColumnForm
|
||||
ref="inlineEditForm"
|
||||
:doc="inlineEditDoc"
|
||||
:fields="inlineEditFields"
|
||||
:column-ratio="columnRatio"
|
||||
:no-border="true"
|
||||
:focus-first-input="true"
|
||||
:autosave="false"
|
||||
@error="(msg) => $emit('error', msg)"
|
||||
/>
|
||||
<div class="flex px-4 pb-2">
|
||||
<Button class="w-1/2 text-gray-900" @click="inlineEditField = null">
|
||||
{{ t`Cancel` }}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
class="ml-2 w-1/2 text-white"
|
||||
@click="() => saveInlineEditDoc(df)"
|
||||
>
|
||||
{{ df.inlineSaveText || t`Save` }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
:key="df.fieldname + '-else'"
|
||||
v-else
|
||||
class="grid"
|
||||
:class="{ 'border-b': !noBorder }"
|
||||
:style="style"
|
||||
>
|
||||
<div class="py-2 pl-4 flex text-gray-600">
|
||||
<div class="py-1">
|
||||
{{ df.label }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="py-2 pr-4"
|
||||
@click="activateInlineEditing(df)"
|
||||
:class="{
|
||||
'pl-2': df.fieldtype === 'AttachImage',
|
||||
}"
|
||||
|
||||
<!-- Inline Field Form (Eg: Address) -->
|
||||
<div
|
||||
v-else-if="renderInline(df)"
|
||||
class="border-b"
|
||||
:key="`${df.fieldname}-inline`"
|
||||
>
|
||||
<TwoColumnForm
|
||||
ref="inlineEditForm"
|
||||
:doc="inlineEditDoc"
|
||||
:fields="inlineEditFields"
|
||||
:column-ratio="columnRatio"
|
||||
:no-border="true"
|
||||
:focus-first-input="true"
|
||||
:autosave="false"
|
||||
@error="(msg) => $emit('error', msg)"
|
||||
/>
|
||||
<div class="flex px-4 pb-2">
|
||||
<Button class="w-1/2 text-gray-900" @click="inlineEditField = null">
|
||||
{{ t`Cancel` }}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
class="ml-2 w-1/2 text-white"
|
||||
@click="() => saveInlineEditDoc(df)"
|
||||
>
|
||||
<FormControl
|
||||
ref="controls"
|
||||
size="small"
|
||||
:df="df"
|
||||
:value="
|
||||
df.inline && doc.getLink(df.fieldname)
|
||||
? doc.getLink(df.fieldname)[
|
||||
doc.getLink(df.fieldname).meta.inlineEditDisplayField ||
|
||||
'name'
|
||||
]
|
||||
: doc[df.fieldname]
|
||||
"
|
||||
:class="{ 'p-2': df.fieldtype === 'Check' }"
|
||||
:read-only="evaluateReadOnly(df)"
|
||||
@change="(value) => onChange(df, value)"
|
||||
@focus="activateInlineEditing(df)"
|
||||
@new-doc="(newdoc) => onChange(df, newdoc.name)"
|
||||
/>
|
||||
<div
|
||||
class="text-sm text-red-600 mt-2 pl-2"
|
||||
v-if="errors[df.fieldname]"
|
||||
>
|
||||
{{ errors[df.fieldname] }}
|
||||
</div>
|
||||
{{ df.inlineSaveText || t`Save` }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Regular Field Form -->
|
||||
<div
|
||||
v-else
|
||||
class="grid"
|
||||
:class="{ 'border-b': !noBorder }"
|
||||
:key="`${df.fieldname}-regular`"
|
||||
:style="style"
|
||||
>
|
||||
<div class="py-2 pl-4 flex text-gray-600">
|
||||
<div class="py-1">
|
||||
{{ df.label }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div
|
||||
class="py-2 pr-4"
|
||||
@click="activateInlineEditing(df)"
|
||||
:class="{
|
||||
'pl-2': df.fieldtype === 'AttachImage',
|
||||
}"
|
||||
>
|
||||
<FormControl
|
||||
ref="controls"
|
||||
size="small"
|
||||
:df="df"
|
||||
:value="getRegularValue(df)"
|
||||
:class="{ 'p-2': df.fieldtype === 'Check' }"
|
||||
:read-only="evaluateReadOnly(df)"
|
||||
@change="(value) => onChange(df, value)"
|
||||
@focus="activateInlineEditing(df)"
|
||||
@new-doc="(newdoc) => onChange(df, newdoc.name)"
|
||||
/>
|
||||
<div
|
||||
class="text-sm text-red-600 mt-2 pl-2"
|
||||
v-if="errors[df.fieldname]"
|
||||
>
|
||||
{{ errors[df.fieldname] }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Button from 'src/components/Button';
|
||||
import FormControl from 'src/components/Controls/FormControl';
|
||||
import Doc from 'fyo/model/doc';
|
||||
import Button from 'src/components/Button.vue';
|
||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||
import { getErrorMessage, handleErrorWithDialog } from 'src/errorHandling';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { getErrorMessage, handleErrorWithDialog } from '../errorHandling';
|
||||
import { evaluateHidden, evaluateReadOnly } from 'src/utils/doc';
|
||||
|
||||
let TwoColumnForm = {
|
||||
export default {
|
||||
name: 'TwoColumnForm',
|
||||
emits: ['error', 'change'],
|
||||
props: {
|
||||
doc: Object,
|
||||
doc: Doc,
|
||||
fields: Array,
|
||||
autosave: Boolean,
|
||||
columnRatio: {
|
||||
@ -120,7 +123,7 @@ let TwoColumnForm = {
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
doctype: this.doc.doctype,
|
||||
schemaName: this.doc.schemaName,
|
||||
name: this.doc.name,
|
||||
doc: this.doc,
|
||||
};
|
||||
@ -134,24 +137,27 @@ let TwoColumnForm = {
|
||||
if (this.focusFirstInput) {
|
||||
this.$refs['controls'][0].focus();
|
||||
}
|
||||
window.tcf = this;
|
||||
},
|
||||
methods: {
|
||||
getRegularValue(df) {
|
||||
if (!df.inline) {
|
||||
return this.doc[df.fieldname];
|
||||
}
|
||||
|
||||
const link = this.doc.getLink(df.fieldname);
|
||||
if (!link) {
|
||||
return this.doc[df.fieldname];
|
||||
}
|
||||
|
||||
const fieldname = link.schema.inlineEditDisplayField ?? 'name';
|
||||
return link[fieldname];
|
||||
},
|
||||
renderInline(df) {
|
||||
return (
|
||||
this.inlineEditField?.fieldname === df?.fieldname && this.inlineEditDoc
|
||||
);
|
||||
},
|
||||
evaluateBoolean(fieldProp, defaultValue) {
|
||||
const type = typeof fieldProp;
|
||||
switch (type) {
|
||||
case 'undefined':
|
||||
return defaultValue;
|
||||
case 'function':
|
||||
return fieldProp(this.doc);
|
||||
default:
|
||||
return !!fieldProp;
|
||||
}
|
||||
},
|
||||
evaluateReadOnly(df) {
|
||||
if (df.fieldname === 'numberSeries' && !this.doc._notInserted) {
|
||||
return true;
|
||||
@ -160,10 +166,8 @@ let TwoColumnForm = {
|
||||
if (this.submitted) {
|
||||
return true;
|
||||
}
|
||||
return this.evaluateBoolean(df.readOnly, false);
|
||||
},
|
||||
evaluateHidden(df) {
|
||||
return this.evaluateBoolean(df.hidden, false);
|
||||
|
||||
return evaluateReadOnly(df, this.doc);
|
||||
},
|
||||
onChange(df, value) {
|
||||
if (value == null || df.inline) {
|
||||
@ -229,8 +233,8 @@ let TwoColumnForm = {
|
||||
}
|
||||
|
||||
this.inlineEditDisplayField =
|
||||
this.doc.meta.inlineEditDisplayField || 'name';
|
||||
this.inlineEditFields = fyo.getMeta(df.target).getQuickEditFields();
|
||||
this.doc.schema.inlineEditDisplayField ?? 'name';
|
||||
this.inlineEditFields = fyo.schemaMap[df.target].quickEditFields ?? [];
|
||||
},
|
||||
async saveInlineEditDoc(df) {
|
||||
if (!this.inlineEditDoc) {
|
||||
@ -248,8 +252,8 @@ let TwoColumnForm = {
|
||||
},
|
||||
computed: {
|
||||
formFields() {
|
||||
return (this.fields || this.doc.meta.getQuickEditFields()).filter(
|
||||
(df) => !this.evaluateHidden(df)
|
||||
return (this.fields || this.doc.quickEditFields).filter(
|
||||
(field) => !evaluateHidden(field, this.doc)
|
||||
);
|
||||
},
|
||||
style() {
|
||||
@ -261,10 +265,8 @@ let TwoColumnForm = {
|
||||
};
|
||||
},
|
||||
submitted() {
|
||||
return Boolean(this.doc.meta.isSubmittable && this.doc.submitted);
|
||||
return Boolean(this.doc.schema.isSubmittable && this.doc.submitted);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default TwoColumnForm;
|
||||
</script>
|
||||
|
@ -54,7 +54,7 @@ import { getTelemetryOptions } from 'fyo/telemetry/helpers';
|
||||
import { TelemetrySetting } from 'fyo/telemetry/types';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import Button from '../Button.vue';
|
||||
import FormControl from '../Controls/FormControl';
|
||||
import FormControl from '../Controls/FormControl.vue';
|
||||
import FeatherIcon from '../FeatherIcon.vue';
|
||||
import HowTo from '../HowTo.vue';
|
||||
import Modal from '../Modal.vue';
|
||||
|
@ -1,15 +1,49 @@
|
||||
import { Fyo } from 'fyo';
|
||||
import { getRegionalModels, models } from 'models';
|
||||
import { getValueMapFromList } from 'utils';
|
||||
|
||||
export const fyo = new Fyo({ isTest: false, isElectron: true });
|
||||
|
||||
export async function initializeModels(dbPath: string, countryCode?: string) {
|
||||
if (countryCode) {
|
||||
async function closeDbIfConnected() {
|
||||
if (!fyo.db.isConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
await fyo.db.close();
|
||||
}
|
||||
|
||||
export async function initializeInstance(
|
||||
dbPath: string,
|
||||
isNew: boolean,
|
||||
countryCode: string
|
||||
) {
|
||||
if (isNew) {
|
||||
await closeDbIfConnected();
|
||||
countryCode = await fyo.db.createNewDatabase(dbPath, countryCode);
|
||||
} else {
|
||||
} else if (!fyo.db.isConnected) {
|
||||
countryCode = await fyo.db.connectToDatabase(dbPath);
|
||||
}
|
||||
|
||||
const regionalModels = await getRegionalModels(countryCode);
|
||||
await fyo.initializeAndRegister(models, regionalModels);
|
||||
|
||||
await setSingles();
|
||||
await setCurrencySymbols();
|
||||
}
|
||||
|
||||
async function setSingles() {
|
||||
await fyo.doc.getSingle('AccountingSettings');
|
||||
await fyo.doc.getSingle('GetStarted');
|
||||
}
|
||||
|
||||
async function setCurrencySymbols() {
|
||||
const currencies = (await fyo.db.getAll('Currency', {
|
||||
fields: ['name', 'symbol'],
|
||||
})) as { name: string; symbol: string }[];
|
||||
|
||||
fyo.currencySymbols = getValueMapFromList(
|
||||
currencies,
|
||||
'name',
|
||||
'symbol'
|
||||
) as Record<string, string | undefined>;
|
||||
}
|
||||
|
@ -338,7 +338,7 @@
|
||||
<script>
|
||||
import { ipcRenderer } from 'electron';
|
||||
import Button from 'src/components/Button.vue';
|
||||
import FormControl from 'src/components/Controls/FormControl';
|
||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
|
||||
import FeatherIcon from 'src/components/FeatherIcon.vue';
|
||||
import HowTo from 'src/components/HowTo.vue';
|
||||
|
@ -1,13 +1,16 @@
|
||||
<template>
|
||||
<div
|
||||
class="py-10 flex-1 bg-white"
|
||||
class="py-10 flex-1 bg-white flex justify-center items-center"
|
||||
:class="{
|
||||
'pointer-events-none': loadingDatabase,
|
||||
'window-drag': platform !== 'Windows',
|
||||
}"
|
||||
>
|
||||
<div class="w-full">
|
||||
<div class="px-12">
|
||||
<div
|
||||
class="w-full w-600 shadow rounded-lg border relative"
|
||||
style="height: 700px"
|
||||
>
|
||||
<div class="px-6 py-8">
|
||||
<h1 class="text-2xl font-semibold">
|
||||
{{ t`Welcome to Frappe Books` }}
|
||||
</h1>
|
||||
@ -20,7 +23,7 @@
|
||||
{{ t`Select a file to load the company transactions` }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="px-12 mt-10 window-no-drag" v-if="!showFiles">
|
||||
<div class="px-12 mt-6 window-no-drag" v-if="!showFiles">
|
||||
<div class="flex">
|
||||
<div
|
||||
@click="newDatabase"
|
||||
@ -135,7 +138,7 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-gray-700">
|
||||
{{ getFileLastModified(file.filePath) }}
|
||||
{{ file.modified }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -148,26 +151,28 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="w-full flex justify-end absolute px-8"
|
||||
style="top: 100%; transform: translateY(-175%)"
|
||||
>
|
||||
<LanguageSelector class="w-28" input-class="text-base" />
|
||||
<div
|
||||
class="w-full flex justify-end absolute px-6 py-6"
|
||||
style="top: 100%; transform: translateY(-100%)"
|
||||
>
|
||||
<LanguageSelector class="w-40" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { ipcRenderer } from 'electron';
|
||||
import fs from 'fs';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { DateTime } from 'luxon';
|
||||
import LanguageSelector from 'src/components/Controls/LanguageSelector.vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { getSavePath } from 'src/utils/ipcCalls';
|
||||
import { IPC_ACTIONS } from 'utils/messages';
|
||||
|
||||
export default {
|
||||
name: 'DatabaseSelector',
|
||||
emits: ['database-connect'],
|
||||
emits: ['file-selected'],
|
||||
data() {
|
||||
return {
|
||||
loadingDatabase: false,
|
||||
@ -187,17 +192,23 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
setFiles() {
|
||||
this.files = fyo.config
|
||||
.get('files', [])
|
||||
.filter(({ filePath }) => fs.existsSync(filePath));
|
||||
this.files = cloneDeep(fyo.config.get('files', [])).filter(
|
||||
({ filePath }) => fs.existsSync(filePath)
|
||||
);
|
||||
|
||||
for (const file of this.files) {
|
||||
const stats = fs.statSync(file.filePath);
|
||||
file.modified = DateTime.fromJSDate(stats.mtime).toRelative();
|
||||
}
|
||||
},
|
||||
async newDatabase() {
|
||||
/*
|
||||
TODO: Refactor this
|
||||
this.fileSelectedFrom = 'New File';
|
||||
let filePath = await createNewDatabase();
|
||||
this.connectToDatabase(filePath);
|
||||
*/
|
||||
const { filePath, canceled } = await getSavePath('books', 'db');
|
||||
if (canceled || !filePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.connectToDatabase(filePath, true);
|
||||
},
|
||||
async existingDatabase() {
|
||||
this.fileSelectedFrom = 'Existing File';
|
||||
@ -214,35 +225,17 @@ export default {
|
||||
this.fileSelectedFrom = file;
|
||||
await this.connectToDatabase(file.filePath);
|
||||
},
|
||||
async connectToDatabase(filePath) {
|
||||
/*
|
||||
TODO: Refactor this
|
||||
async connectToDatabase(filePath, isNew) {
|
||||
if (!filePath) {
|
||||
return;
|
||||
}
|
||||
this.loadingDatabase = true;
|
||||
const { connectionSuccess, reason } = await connectToLocalDatabase(
|
||||
filePath
|
||||
);
|
||||
this.loadingDatabase = false;
|
||||
if (connectionSuccess) {
|
||||
this.$emit('database-connect');
|
||||
|
||||
if (isNew) {
|
||||
this.$emit('file-selected', filePath, isNew);
|
||||
return;
|
||||
}
|
||||
const title = this.t`DB Connection Error`;
|
||||
let content =
|
||||
this.t`Please select an existing database or create a new one.` +
|
||||
` reason: ${reason}, filePath: ${filePath}`;
|
||||
if (reason === DB_CONN_FAILURE.CANT_OPEN) {
|
||||
content = this
|
||||
.t`Can't open database file: ${filePath}. Please create a new file.`;
|
||||
}
|
||||
await showErrorDialog(title, content);
|
||||
*/
|
||||
},
|
||||
getFileLastModified(filePath) {
|
||||
let stats = fs.statSync(filePath);
|
||||
return DateTime.fromJSDate(stats.mtime).toRelative();
|
||||
|
||||
this.$emit('file-selected', filePath, !!isNew);
|
||||
},
|
||||
},
|
||||
components: { LanguageSelector },
|
||||
|
@ -201,7 +201,7 @@
|
||||
import { getInvoiceStatus } from 'models/helpers';
|
||||
import BackLink from 'src/components/BackLink';
|
||||
import Button from 'src/components/Button';
|
||||
import FormControl from 'src/components/Controls/FormControl';
|
||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||
import DropdownWithActions from 'src/components/DropdownWithActions';
|
||||
import PageHeader from 'src/components/PageHeader';
|
||||
import StatusBadge from 'src/components/StatusBadge';
|
||||
|
@ -130,7 +130,7 @@
|
||||
<script>
|
||||
import BackLink from 'src/components/BackLink';
|
||||
import Button from 'src/components/Button';
|
||||
import FormControl from 'src/components/Controls/FormControl';
|
||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||
import DropdownWithActions from 'src/components/DropdownWithActions';
|
||||
import PageHeader from 'src/components/PageHeader';
|
||||
import StatusBadge from 'src/components/StatusBadge';
|
||||
|
@ -79,7 +79,7 @@
|
||||
<script>
|
||||
import { t } from 'fyo';
|
||||
import Button from 'src/components/Button';
|
||||
import FormControl from 'src/components/Controls/FormControl';
|
||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||
import DropdownWithActions from 'src/components/DropdownWithActions';
|
||||
import StatusBadge from 'src/components/StatusBadge';
|
||||
import TwoColumnForm from 'src/components/TwoColumnForm';
|
||||
|
@ -139,7 +139,7 @@
|
||||
import { getReportData } from 'reports/index';
|
||||
import reportViewConfig from 'reports/view';
|
||||
import Button from 'src/components/Button';
|
||||
import FormControl from 'src/components/Controls/FormControl';
|
||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
|
||||
import FeatherIcon from 'src/components/FeatherIcon.vue';
|
||||
import PageHeader from 'src/components/PageHeader';
|
||||
|
@ -47,7 +47,7 @@
|
||||
</template>
|
||||
<script>
|
||||
import { ipcRenderer } from 'electron';
|
||||
import FormControl from 'src/components/Controls/FormControl';
|
||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||
import TwoColumnForm from 'src/components/TwoColumnForm';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { IPC_ACTIONS } from 'utils/messages';
|
||||
|
@ -41,7 +41,7 @@
|
||||
import { ConfigKeys } from 'fyo/core/types';
|
||||
import { getTelemetryOptions } from 'fyo/telemetry/helpers';
|
||||
import { TelemetrySetting } from 'fyo/telemetry/types';
|
||||
import FormControl from 'src/components/Controls/FormControl';
|
||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||
import LanguageSelector from 'src/components/Controls/LanguageSelector.vue';
|
||||
import TwoColumnForm from 'src/components/TwoColumnForm';
|
||||
import { fyo } from 'src/initFyo';
|
||||
|
@ -1,28 +1,24 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex-1 py-10 bg-white h-screen"
|
||||
:class="{
|
||||
'window-drag': platform !== 'Windows',
|
||||
}"
|
||||
>
|
||||
<div class="px-12">
|
||||
<div class="w-600 shadow rounded-lg border relative" style="height: 700px">
|
||||
<div class="px-6 py-8">
|
||||
<h1 class="text-2xl font-semibold"><slot name="title"></slot></h1>
|
||||
</div>
|
||||
|
||||
<div class="px-8 mt-5 window-no-drag">
|
||||
<div class="px-6 window-no-drag">
|
||||
<slot name="content"></slot>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex justify-between px-8 mt-5 window-no-drag absolute w-full"
|
||||
style="top: 100%; transform: translateY(-260%)"
|
||||
class="flex justify-between px-6 pb-6 window-no-drag absolute w-600"
|
||||
style="top: 100%; transform: translateY(-100%)"
|
||||
>
|
||||
<Button class="text-sm text-grey-900" @click="$emit('secondary-clicked')">
|
||||
<Button class="text-sm text-grey-900 w-28" @click="$emit('secondary-clicked')">
|
||||
<slot name="secondaryButton"></slot>
|
||||
</Button>
|
||||
<Button
|
||||
@click="$emit('primary-clicked')"
|
||||
type="primary"
|
||||
class="text-sm text-white"
|
||||
class="text-sm text-white w-28"
|
||||
:disabled="primaryDisabled"
|
||||
>
|
||||
<slot name="primaryButton"></slot>
|
||||
|
@ -1,39 +0,0 @@
|
||||
import { createNumberSeries } from 'fyo/model/naming';
|
||||
import { DEFAULT_SERIES_START } from 'fyo/utils/consts';
|
||||
import { getValueMapFromList } from 'utils';
|
||||
import { fyo } from './initFyo';
|
||||
|
||||
export async function postStart() {
|
||||
await createDefaultNumberSeries();
|
||||
await setSingles();
|
||||
await setCurrencySymbols();
|
||||
}
|
||||
|
||||
async function createDefaultNumberSeries() {
|
||||
await createNumberSeries('SINV-', 'SalesInvoice', DEFAULT_SERIES_START, fyo);
|
||||
await createNumberSeries(
|
||||
'PINV-',
|
||||
'PurchaseInvoice',
|
||||
DEFAULT_SERIES_START,
|
||||
fyo
|
||||
);
|
||||
await createNumberSeries('PAY-', 'Payment', DEFAULT_SERIES_START, fyo);
|
||||
await createNumberSeries('JV-', 'JournalEntry', DEFAULT_SERIES_START, fyo);
|
||||
}
|
||||
|
||||
async function setSingles() {
|
||||
await fyo.doc.getSingle('AccountingSettings');
|
||||
await fyo.doc.getSingle('GetStarted');
|
||||
}
|
||||
|
||||
async function setCurrencySymbols() {
|
||||
const currencies = (await fyo.db.getAll('Currency', {
|
||||
fields: ['name', 'symbol'],
|
||||
})) as { name: string; symbol: string }[];
|
||||
|
||||
fyo.currencySymbols = getValueMapFromList(
|
||||
currencies,
|
||||
'name',
|
||||
'symbol'
|
||||
) as Record<string, string | undefined>;
|
||||
}
|
@ -1,8 +1,13 @@
|
||||
import countryInfo from 'fixtures/countryInfo.json';
|
||||
import { ConfigFile, DocValueMap } from 'fyo/core/types';
|
||||
import Doc from 'fyo/model/doc';
|
||||
import { createNumberSeries } from 'fyo/model/naming';
|
||||
import { getId } from 'fyo/telemetry/helpers';
|
||||
import { DEFAULT_CURRENCY, DEFAULT_LOCALE } from 'fyo/utils/consts';
|
||||
import {
|
||||
DEFAULT_CURRENCY,
|
||||
DEFAULT_LOCALE,
|
||||
DEFAULT_SERIES_START,
|
||||
} from 'fyo/utils/consts';
|
||||
import { AccountingSettings } from 'models/baseModels/AccountingSettings/AccountingSettings';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { createRegionalRecords } from 'src/regional';
|
||||
@ -21,6 +26,7 @@ export default async function setupInstance(
|
||||
await createCurrencyRecords();
|
||||
await createAccountRecords(bankName, country, chartOfAccounts);
|
||||
await createRegionalRecords(country);
|
||||
await createDefaultNumberSeries();
|
||||
|
||||
await completeSetup(companyName);
|
||||
}
|
||||
@ -215,3 +221,15 @@ async function getBankAccountParentName(country: string) {
|
||||
|
||||
return parentBankAccount[0].name;
|
||||
}
|
||||
|
||||
async function createDefaultNumberSeries() {
|
||||
await createNumberSeries('SINV-', 'SalesInvoice', DEFAULT_SERIES_START, fyo);
|
||||
await createNumberSeries(
|
||||
'PINV-',
|
||||
'PurchaseInvoice',
|
||||
DEFAULT_SERIES_START,
|
||||
fyo
|
||||
);
|
||||
await createNumberSeries('PAY-', 'Payment', DEFAULT_SERIES_START, fyo);
|
||||
await createNumberSeries('JV-', 'JournalEntry', DEFAULT_SERIES_START, fyo);
|
||||
}
|
||||
|
@ -51,4 +51,8 @@ html {
|
||||
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.w-600 {
|
||||
width: 600px;
|
||||
}
|
||||
|
28
src/utils/doc.ts
Normal file
28
src/utils/doc.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import Doc from 'fyo/model/doc';
|
||||
import { Field } from 'schemas/types';
|
||||
|
||||
export function evaluateReadOnly(field: Field, doc: Doc) {
|
||||
if (field.readOnly !== undefined) {
|
||||
return field.readOnly;
|
||||
}
|
||||
|
||||
const readOnlyFunc = doc.readOnly[field.fieldname];
|
||||
if (readOnlyFunc !== undefined) {
|
||||
return readOnlyFunc();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function evaluateHidden(field: Field, doc: Doc) {
|
||||
if (field.hidden !== undefined) {
|
||||
return field.hidden;
|
||||
}
|
||||
|
||||
const hiddenFunction = doc.hidden[field.fieldname];
|
||||
if (hiddenFunction !== undefined) {
|
||||
return hiddenFunction();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
@ -1,4 +1,9 @@
|
||||
import { getSingleValue } from 'fyo/utils';
|
||||
import { DateTime } from 'luxon';
|
||||
import { SetupWizard } from 'models/baseModels/SetupWizard/SetupWizard';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import SetupWizardSchema from 'schemas/app/SetupWizard.json';
|
||||
import { Schema } from 'schemas/types';
|
||||
import { fyo } from 'src/initFyo';
|
||||
|
||||
export async function getDatesAndPeriodicity(
|
||||
@ -25,3 +30,24 @@ export async function getDatesAndPeriodicity(
|
||||
periodicity,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getSetupWizardDoc() {
|
||||
/**
|
||||
* This is used cause when setup wizard is running
|
||||
* the database isn't yet initialized.
|
||||
*/
|
||||
return await fyo.doc.getNewDoc(
|
||||
'SetupWizard',
|
||||
{},
|
||||
SetupWizardSchema as Schema,
|
||||
SetupWizard
|
||||
);
|
||||
}
|
||||
|
||||
export async function getSetupComplete(): Promise<boolean> {
|
||||
return !!(await getSingleValue(
|
||||
'setupComplete',
|
||||
ModelNameEnum.AccountingSettings,
|
||||
fyo
|
||||
));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user