2
0
mirror of https://github.com/frappe/books.git synced 2024-11-09 23:30:56 +00:00

incr: fix a bunch of vue files

- no modes in the first pane
This commit is contained in:
18alantom 2022-04-28 18:07:07 +05:30
parent 1bcd0f0afb
commit a2cd1bac21
21 changed files with 361 additions and 287 deletions

View File

@ -348,7 +348,9 @@ export default class DatabaseCore extends DatabaseBase {
}
async #removeColumns(schemaName: string, targetColumns: string[]) {
// TODO: Implement this for sqlite
const fields = this.schemaMap[schemaName]?.fields.map((f) => f.fieldname);
const tableRows = await this.getAll(schemaName, { fields });
this.prestigeTheTable(schemaName, tableRows);
}
#getError(err: Error) {

View File

@ -88,6 +88,8 @@ export class Converter {
return toRawFloat(value, field);
case FieldTypeEnum.Check:
return toRawCheck(value, field);
case FieldTypeEnum.Link:
return toRawLink(value, field);
default:
return toRawString(value, field);
}
@ -335,6 +337,18 @@ function toRawString(value: DocValue, field: Field): string | null {
throwError(value, field, 'raw');
}
function toRawLink(value: DocValue, field: Field): string | null {
if (value === null || !(value as string)?.length) {
return null;
}
if (typeof value === 'string') {
return value;
}
throwError(value, field, 'raw');
}
function throwError<T>(value: T, field: Field, type: 'raw' | 'doc'): never {
throw new ValueError(
`invalid ${type} conversion '${value}' of type ${typeof value} found, field: ${JSON.stringify(

View File

@ -6,7 +6,7 @@ import {
ConflictError,
MandatoryError,
NotFoundError,
ValidationError,
ValidationError
} from 'fyo/utils/errors';
import Observable from 'fyo/utils/observable';
import Money from 'pesa/dist/types/src/money';
@ -15,7 +15,7 @@ import {
FieldTypeEnum,
OptionField,
Schema,
TargetField,
TargetField
} from 'schemas/types';
import { getIsNullOrUndef, getMapFromList, getRandomString } from 'utils';
import { markRaw } from 'vue';
@ -24,7 +24,7 @@ import {
areDocValuesEqual,
getMissingMandatoryMessage,
getPreDefaultValues,
shouldApplyFormula,
shouldApplyFormula
} from './helpers';
import { setName } from './naming';
import {
@ -42,7 +42,7 @@ import {
ReadOnlyMap,
RequiredMap,
TreeViewSettings,
ValidationMap,
ValidationMap
} from './types';
import { validateOptions, validateRequired } from './validationFunction';
@ -139,14 +139,16 @@ export class Doc extends Observable<DocValue | Doc[]> {
}
// set value and trigger change
async set(fieldname: string | DocValueMap, value?: DocValue | Doc[]) {
async set(
fieldname: string | DocValueMap,
value?: DocValue | Doc[]
): Promise<boolean> {
if (typeof fieldname === 'object') {
await this.setMultiple(fieldname as DocValueMap);
return;
return await this.setMultiple(fieldname as DocValueMap);
}
if (!this._canSet(fieldname, value)) {
return;
return false;
}
this._setDirty(true);
@ -168,12 +170,19 @@ export class Doc extends Observable<DocValue | Doc[]> {
} else {
await this._applyChange(fieldname);
}
return true;
}
async setMultiple(docValueMap: DocValueMap) {
async setMultiple(docValueMap: DocValueMap): Promise<boolean> {
let hasSet = false;
for (const fieldname in docValueMap) {
await this.set(fieldname, docValueMap[fieldname] as DocValue | Doc[]);
hasSet ||= await this.set(
fieldname,
docValueMap[fieldname] as DocValue | Doc[]
);
}
return hasSet;
}
_canSet(fieldname: string, value?: DocValue | Doc[]): boolean {
@ -197,12 +206,14 @@ export class Doc extends Observable<DocValue | Doc[]> {
return !areDocValuesEqual(currentValue as DocValue, value as DocValue);
}
async _applyChange(fieldname: string) {
async _applyChange(fieldname: string): Promise<boolean> {
await this._applyFormula(fieldname);
await this.trigger('change', {
doc: this,
changed: fieldname,
});
return true;
}
_setDefaults() {
@ -232,13 +243,29 @@ export class Doc extends Observable<DocValue | Doc[]> {
// push child row and trigger change
this.push(fieldname, docValueMap);
this._dirty = true;
await this._applyChange(fieldname);
return await this._applyChange(fieldname);
}
async remove(fieldname: string, idx: number) {
let rows = this[fieldname] as Doc[] | undefined;
if (!Array.isArray(rows)) {
return;
}
this._setDirty(true);
rows = rows.filter((row, i) => row.idx !== idx || i !== idx)!;
rows.forEach((row, i) => {
row.idx = i;
});
this[fieldname] = rows;
return await this._applyChange(fieldname);
}
push(fieldname: string, docValueMap: Doc | DocValueMap = {}) {
// push child row without triggering change
this[fieldname] ??= [];
const childDoc = this._getChildDoc(docValueMap, fieldname);
childDoc.parentdoc = this;
(this[fieldname] as Doc[]).push(childDoc);
}
@ -389,6 +416,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
throw new NotFoundError(`Not Found: ${this.schemaName} ${this.name}`);
}
this._setDirty(false);
this._notInserted = false;
this.fyo.doc.observer.trigger(`load:${this.schemaName}`, this.name);
}
@ -744,9 +772,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
updateMap.name = updateMap.name + ' CPY';
}
const doc = this.fyo.doc.getNewDoc(this.schemaName, {}, false);
await doc.setMultiple(updateMap);
const doc = this.fyo.doc.getNewDoc(this.schemaName, updateMap, false);
if (shouldSync) {
await doc.sync();
}

View File

@ -52,7 +52,7 @@ export function getDeviceId(fyo: Fyo): UniqueId {
}
export function getInstanceId(fyo: Fyo): UniqueId {
const files = fyo.config.get(ConfigKeys.Files) as ConfigFile[];
const files = (fyo.config.get(ConfigKeys.Files) ?? []) as ConfigFile[];
const companyName = fyo.singles.AccountingSettings?.companyName as string;
if (companyName === undefined) {

View File

@ -129,7 +129,7 @@ function getNumberFormatter(fyo: Fyo) {
}
function getCurrency(field: Field, doc: Doc | null, fyo: Fyo): string {
let getCurrency = doc?.getCurrencies[field.fieldname];
let getCurrency = doc?.getCurrencies?.[field.fieldname];
if (getCurrency !== undefined) {
return getCurrency();
}

View File

@ -67,7 +67,7 @@ export class Party extends Doc {
},
currency: async () =>
this.fyo.singles.AccountingSettings!.currency as string,
addressDisplay: async () => {
address: async () => {
const address = this.address as string | undefined;
if (address) {
return this.getFrom('Address', address, 'addressDisplay') as string;

View File

@ -1 +1,6 @@
export type PartyRole = 'Both' | 'Supplier' | 'Customer';
export enum PartyRoleEnum {
'Both' = 'Both',
'Supplier' = 'Supplier',
'Customer' = 'Customer',
}

View File

@ -1,6 +1,6 @@
import { LedgerPosting } from 'models/ledgerPosting/ledgerPosting';
import { Fyo } from 'fyo';
import { Action, ListViewSettings } from 'fyo/model/types';
import { LedgerPosting } from 'models/ledgerPosting/ledgerPosting';
import {
getTransactionActions,
getTransactionStatusColumn,
@ -40,13 +40,13 @@ export class PurchaseInvoice extends Invoice {
return getTransactionActions('PurchaseInvoice', fyo);
}
static getListViewSettings(fyo: Fyo): ListViewSettings {
static getListViewSettings(): ListViewSettings {
return {
formRoute: (name) => `/edit/PurchaseInvoice/${name}`,
columns: [
'party',
'name',
getTransactionStatusColumn(fyo),
getTransactionStatusColumn(),
'date',
'grandTotal',
'outstandingAmount',

View File

@ -1,6 +1,6 @@
import { LedgerPosting } from 'models/ledgerPosting/ledgerPosting';
import { Fyo } from 'fyo';
import { Action, ListViewSettings } from 'fyo/model/types';
import { LedgerPosting } from 'models/ledgerPosting/ledgerPosting';
import {
getTransactionActions,
getTransactionStatusColumn,
@ -38,13 +38,13 @@ export class SalesInvoice extends Invoice {
return getTransactionActions('SalesInvoice', fyo);
}
static getListViewSettings(fyo: Fyo): ListViewSettings {
static getListViewSettings(): ListViewSettings {
return {
formRoute: (name) => `/edit/SalesInvoice/${name}`,
columns: [
'party',
'name',
getTransactionStatusColumn(fyo),
getTransactionStatusColumn(),
'date',
'grandTotal',
'outstandingAmount',

View File

@ -1,4 +1,4 @@
import { Fyo } from 'fyo';
import { Fyo, t } from 'fyo';
import { Doc } from 'fyo/model/doc';
import { Action, ColumnConfig } from 'fyo/model/types';
import { NotFoundError } from 'fyo/utils/errors';
@ -75,16 +75,16 @@ export function getTransactionActions(schemaName: string, fyo: Fyo): Action[] {
];
}
export function getTransactionStatusColumn(fyo: Fyo): ColumnConfig {
export function getTransactionStatusColumn(): ColumnConfig {
const statusMap = {
Unpaid: fyo.t`Unpaid`,
Paid: fyo.t`Paid`,
Draft: fyo.t`Draft`,
Cancelled: fyo.t`Cancelled`,
Unpaid: t`Unpaid`,
Paid: t`Paid`,
Draft: t`Draft`,
Cancelled: t`Cancelled`,
};
return {
label: fyo.t`Status`,
label: t`Status`,
fieldname: 'status',
fieldtype: 'Select',
render(doc: Doc) {

View File

@ -79,6 +79,13 @@
"fieldtype": "Data"
}
],
"quickEditFields": ["email", "role", "phone", "address", "defaultAccount", "currency", "taxId"],
"quickEditFields": [
"email",
"phone",
"address",
"defaultAccount",
"currency",
"taxId"
],
"keywordFields": ["name"]
}

View File

@ -20,7 +20,7 @@ export default {
inputClass: {
type: String,
default:
'bg-gray-100 active:bg-gray-200 focus:bg-gray-200 px-3 py-2 text-base',
'px-3 py-2 text-base',
},
dontReload: {
type: Boolean,

View File

@ -21,7 +21,7 @@ export default {
const filters = await this.getFilters(keyword);
if (keyword && !filters.keywords) {
filters.keywords = ['like', keyword];
filters[schema.titleField] = ['like', keyword];
}
const fields = [

View File

@ -3,8 +3,10 @@
<div class="text-gray-600 text-sm mb-1" v-if="showLabel">
{{ df.label }}
</div>
<!-- Title Row -->
<Row :ratio="ratio" class="border-b px-2 text-gray-600 w-full">
<div class="flex items-center pl-2">No</div>
<div class="flex items-center pl-2">#</div>
<div
:class="{
'px-2 py-3': size === 'small',
@ -17,6 +19,8 @@
{{ df.label }}
</div>
</Row>
<!-- Data Rows -->
<div class="overflow-auto" :style="{ 'max-height': rowContainerHeight }">
<TableRow
:class="{ 'pointer-events-none': isReadOnly }"
@ -27,6 +31,8 @@
@remove="removeRow(row)"
/>
</div>
<!-- Add Row and Row Count -->
<Row
:ratio="ratio"
class="text-gray-500 cursor-pointer border-transparent px-2 w-full"
@ -54,7 +60,7 @@
}"
v-if="maxRowsBeforeOverflow && value.length > maxRowsBeforeOverflow"
>
{{ value.length }} rows
{{ t`${value.length} rows` }}
</div>
</Row>
</div>
@ -71,9 +77,11 @@ export default {
extends: Base,
props: {
showHeader: {
type: Boolean,
default: true,
},
maxRowsBeforeOverflow: {
type: Number,
default: 0,
},
},
@ -81,6 +89,7 @@ export default {
Row,
TableRow,
},
inject: ['doc'],
data: () => ({ rowContainerHeight: null }),
watch: {
value: {
@ -101,19 +110,28 @@ export default {
methods: {
focus() {},
addRow() {
let rows = this.value || [];
this.triggerChange([...rows, {}]);
this.$nextTick(() => {
this.scrollToRow(this.value.length - 1);
this.doc.append(this.df.fieldname, {}).then((s) => {
if (!s) {
return;
}
this.$nextTick(() => {
this.scrollToRow(this.value.length - 1);
});
this.triggerChange(this.value);
});
},
removeRow(row) {
let rows = this.value || [];
rows = rows.filter((_row) => _row !== row);
this.triggerChange(rows);
this.doc.remove(this.df.fieldname, row.idx).then((s) => {
if (!s) {
return;
}
this.triggerChange(this.value);
});
},
scrollToRow(index) {
let row = this.$refs['table-row'][index];
const row = this.$refs['table-row'][index];
row && row.$el.scrollIntoView({ block: 'nearest' });
},
},

View File

@ -1,5 +1,6 @@
<template>
<Row :ratio="ratio" class="w-full px-2 border-b hover:bg-brand-100 group">
<!-- Index or Remove button -->
<div class="flex items-center pl-2 text-gray-600">
<span class="hidden group-hover:inline-block">
<feather-icon
@ -12,6 +13,8 @@
{{ row.idx + 1 }}
</span>
</div>
<!-- Data Input Form Control -->
<FormControl
:size="size"
class="py-2"
@ -23,6 +26,8 @@
@change="(value) => onChange(df, value)"
@new-doc="(doc) => row.set(df.fieldname, doc.name)"
/>
<!-- Error Display -->
<div
class="text-sm text-red-600 mb-2 pl-2 col-span-full"
v-if="Object.values(errors).length"
@ -32,13 +37,20 @@
</Row>
</template>
<script>
import { Doc } from 'fyo/model/doc';
import Row from 'src/components/Row.vue';
import { getErrorMessage } from 'src/utils';
import FormControl from './FormControl.vue';
export default {
name: 'TableRow',
props: ['row', 'tableFields', 'size', 'ratio', 'isNumeric'],
props: {
row: Doc,
tableFields: Array,
size: String,
ratio: Array,
isNumeric: Function,
},
emits: ['remove'],
components: {
Row,
@ -50,7 +62,7 @@ export default {
},
provide() {
return {
doctype: this.row.doctype,
schemaName: this.row.schemaName,
name: this.row.name,
doc: this.row,
};
@ -62,11 +74,6 @@ export default {
}
this.errors[df.fieldname] = null;
const oldValue = this.row.get(df.fieldname);
if (oldValue === value) {
return;
}
this.row.set(df.fieldname, value).catch((e) => {
this.errors[df.fieldname] = getErrorMessage(e, this.row);
});

View File

@ -117,6 +117,7 @@ export default {
return {
inlineEditDoc: null,
inlineEditField: null,
formFields: [],
errors: {},
};
},
@ -133,8 +134,9 @@ export default {
TwoColumnForm: () => TwoColumnForm,
},
mounted() {
this.setFormFields();
if (this.focusFirstInput) {
this.$refs['controls'][0].focus();
this.$refs['controls']?.[0].focus();
}
if (fyo.store.isDevelopment) {
@ -176,28 +178,34 @@ export default {
return;
}
const oldValue = this.doc.get(df.fieldname);
this.errors[df.fieldname] = null;
if (oldValue === value) {
return;
}
if (this.emitChange) {
this.$emit('change', df, value, oldValue);
}
// handle rename
if (this.autosave && df.fieldname === 'name' && !this.doc.notInserted) {
return this.doc.rename(value);
}
this.onChangeCommon(df, value);
const oldValue = this.doc.get(df.fieldname);
this.errors[df.fieldname] = null;
this.onChangeCommon(df, value, oldValue);
},
onChangeCommon(df, value) {
this.doc.set(df.fieldname, value).catch((e) => {
// set error message for this field
this.errors[df.fieldname] = getErrorMessage(e, this.doc);
});
onChangeCommon(df, value, oldValue) {
this.doc
.set(df.fieldname, value)
.catch((e) => {
this.errors[df.fieldname] = getErrorMessage(e, this.doc);
})
.then((success) => {
if (!success) {
return;
}
this.handlePostSet(df, value, oldValue);
});
},
handlePostSet(df, value, oldValue) {
this.setFormFields();
if (this.emitChange) {
this.$emit('change', df, value, oldValue);
}
if (this.autosave && this.doc.dirty) {
if (df.fieldtype === 'Table') {
@ -250,15 +258,14 @@ export default {
await this.stopInlineEditing();
},
async stopInlineEditing() {
if (this.inlineEditDoc?.dirty) {
if (this.inlineEditDoc?.dirty && !this.inlineEditDoc?.notInserted) {
await this.inlineEditDoc.load();
}
this.inlineEditDoc = null;
this.inlineEditField = null;
},
},
computed: {
formFields() {
setFormFields() {
let fieldList = this.fields;
if (fieldList.length === 0) {
@ -269,10 +276,12 @@ export default {
fieldList = this.doc.schema.fields.filter((f) => f.required);
}
return fieldList.filter(
this.formFields = fieldList.filter(
(field) => field && !evaluateHidden(field, this.doc)
);
},
},
computed: {
style() {
let templateColumns = (this.columnRatio || [1, 1])
.map((r) => `${r}fr`)

View File

@ -20,16 +20,14 @@
</div>
<div class="flex justify-between">
<span>
{{ fyo.format(invoice.date, invoiceMeta.getField('date')) }}
{{ fyo.format(invoice.date, getInvoiceField(invoice, 'date')) }}
</span>
<div>
<span
class="font-medium text-gray-900"
>
<span class="font-medium text-gray-900">
{{
fyo.format(
amountPaid(invoice),
invoiceMeta.getField('baseGrandTotal')
getInvoiceField(invoice, 'baseGrandTotal')
)
}}
</span>
@ -37,7 +35,7 @@
({{
fyo.format(
invoice.outstandingAmount,
invoiceMeta.getField('outstandingAmount')
getInvoiceField(invoice, 'outstandingAmount')
)
}})
</span>
@ -49,59 +47,74 @@
</template>
<script>
import { Doc } from 'fyo/model/doc';
import { PartyRoleEnum } from 'models/baseModels/Party/types';
import { getTransactionStatusColumn } from 'models/helpers';
import { ModelNameEnum } from 'models/types';
import { fyo } from 'src/initFyo';
import { routeTo } from 'src/utils';
// import { getStatusColumn } from '../Transaction/Transaction';
export default {
name: 'PartyWidget',
props: ['doc'],
props: { doc: Doc },
data() {
return {
pendingInvoices: [],
};
},
computed: {
invoiceDoctype() {
let isCustomer = this.doc.doctype === 'Customer';
return isCustomer ? 'SalesInvoice' : 'PurchaseInvoice';
},
invoiceMeta() {
return fyo.getMeta(this.invoiceDoctype);
invoiceSchemaNames() {
switch (this.doc.get('role')) {
case PartyRoleEnum.Customer:
return [ModelNameEnum.SalesInvoice];
case PartyRoleEnum.Supplier:
return [ModelNameEnum.PurchaseInvoice];
case PartyRoleEnum.Both:
default:
return [ModelNameEnum.SalesInvoice, ModelNameEnum.PurchaseInvoice];
}
},
},
mounted() {
this.fetchPendingInvoices();
},
methods: {
getInvoiceField(invoice, fieldname) {
return fyo.getField(invoice.schemaName, fieldname);
},
async fetchPendingInvoices() {
let isCustomer = this.doc.doctype === 'Customer';
let doctype = this.invoiceDoctype;
let partyField = isCustomer ? 'customer' : 'supplier';
this.pendingInvoices = await fyo.db.getAll({
doctype,
fields: [
'name',
'date',
'outstandingAmount',
'baseGrandTotal',
'submitted',
],
filters: {
[partyField]: this.doc.name,
},
limit: 3,
orderBy: 'created',
});
window.pendingInvoices = this.pendingInvoices;
const pendingInvoices = [];
for (const schemaName of this.invoiceSchemaNames) {
const invoices = await fyo.db.getAll(schemaName, {
fields: [
'name',
'date',
'outstandingAmount',
'baseGrandTotal',
'submitted',
],
filters: {
party: this.doc.name,
},
limit: 3,
orderBy: 'created',
});
invoices.forEach((i) => {
i.schemaName = schemaName;
});
pendingInvoices.push(...invoices);
}
this.pendingInvoices = pendingInvoices;
},
getStatusBadge(doc) {
// let statusColumn = getStatusColumn();
// return statusColumn.render(doc);
return {}
const statusColumn = getTransactionStatusColumn();
return statusColumn.render(doc);
},
routeToForm(doc) {
routeTo(`/edit/${this.invoiceDoctype}/${doc.name}`);
routeToForm(invoice) {
routeTo(`/edit/${invoice.schemaName}/${invoice.name}`);
},
fullyPaid(invoice) {
return invoice.outstandingAmount.isZero();

View File

@ -10,147 +10,121 @@
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">
<!-- Welcome to Frappe Books -->
<div class="px-6 py-10">
<h1 class="text-2xl font-semibold select-none">
{{ t`Welcome to Frappe Books` }}
</h1>
<p class="text-gray-600 text-base" v-if="!showFiles">
<p class="text-gray-600 text-base select-none">
{{
t`Create a new file or select an existing one from your computer`
}}
</p>
<p class="text-gray-600 text-base" v-if="showFiles">
{{ t`Select a file to load the company transactions` }}
</p>
</div>
<div class="px-12 mt-6 window-no-drag" v-if="!showFiles">
<div class="flex">
<div
@click="newDatabase"
class="
w-1/2
border
rounded-xl
flex flex-col
items-center
py-8
px-5
cursor-pointer
hover:shadow
"
>
<div
class="w-14 h-14 rounded-full bg-blue-200 relative flex-center"
>
<div
class="w-12 h-12 absolute rounded-full bg-blue-500 flex-center"
>
<feather-icon name="plus" class="text-white w-5 h-5" />
</div>
</div>
<div class="mt-5 font-medium">
<template
v-if="loadingDatabase && fileSelectedFrom === 'New File'"
>
{{ t`Loading...` }}
</template>
<template v-else>
{{ t`New File` }}
</template>
</div>
<div class="mt-2 text-sm text-gray-600 text-center">
{{ t`Create a new file and store it in your computer.` }}
</div>
</div>
<div
@click="existingDatabase"
class="
ml-6
w-1/2
border
rounded-xl
flex flex-col
items-center
py-8
px-5
cursor-pointer
hover:shadow
"
>
<div
class="w-14 h-14 rounded-full bg-green-200 relative flex-center"
>
<div class="w-12 h-12 rounded-full bg-green-500 flex-center">
<feather-icon name="upload" class="w-4 h-4 text-white" />
</div>
</div>
<div class="mt-5 font-medium">
<template
v-if="loadingDatabase && fileSelectedFrom === 'Existing File'"
>
{{ t`Loading...` }}
</template>
<template v-else>
{{ t`Existing File` }}
</template>
</div>
<div class="mt-2 text-sm text-gray-600 text-center">
{{ t`Load an existing .db file from your computer.` }}
</div>
</div>
</div>
<a
v-if="files.length > 0"
class="text-brand text-sm mt-4 inline-block cursor-pointer"
@click="showFiles = true"
>
{{ t`Select from existing files` }}
</a>
</div>
<div v-if="showFiles">
<div class="px-12 mt-6">
<div
class="
py-2
px-4
text-sm
flex
justify-between
items-center
hover:bg-gray-100
cursor-pointer
border-b
"
:class="{ 'border-t': i === 0 }"
v-for="(file, i) in files"
:key="file.filePath"
@click="selectFile(file)"
>
<div class="flex items-baseline">
<span>
<template v-if="loadingDatabase && fileSelectedFrom === file">
{{ t`Loading...` }}
</template>
<template v-else>
{{ file.companyName }}
</template>
</span>
</div>
<div class="text-gray-700">
{{ file.modified }}
</div>
</div>
<hr />
<!-- New File (Blue Icon) -->
<div
@click="newDatabase"
class="
px-6
h-18
flex flex-row
items-center
cursor-pointer
gap-4
p-2
hover:bg-gray-100
"
>
<div class="w-8 h-8 rounded-full bg-blue-500 relative flex-center">
<feather-icon name="plus" class="text-white w-5 h-5" />
</div>
<div class="px-12 mt-4">
<a
class="text-brand text-sm cursor-pointer"
@click="showFiles = false"
>
{{ t`Select file manually` }}
</a>
<div>
<p class="font-medium">
{{ t`New File` }}
</p>
<p class="text-sm text-gray-600">
{{ t`Create a new file and store it in your computer.` }}
</p>
</div>
</div>
<!-- Existing File (Green Icon) -->
<div
@click="existingDatabase"
class="
px-6
h-18
flex flex-row
items-center
cursor-pointer
gap-4
p-2
hover:bg-gray-100
"
>
<div class="w-8 h-8 rounded-full bg-green-500 relative flex-center">
<feather-icon name="upload" class="w-4 h-4 text-white" />
</div>
<div>
<p class="font-medium">
{{ t`Existing File` }}
</p>
<p class="text-sm text-gray-600">
{{ t`Load an existing .db file from your computer.` }}
</p>
</div>
</div>
<hr />
<!-- File List -->
<div class="overflow-scroll" style="max-height: 340px">
<div
class="
h-18
px-6
flex
gap-4
items-center
hover:bg-gray-100
cursor-pointer
"
v-for="(file, i) in files"
:key="file.dbPath"
@click="selectFile(file)"
>
<div
class="
w-8
h-8
rounded-full
flex
justify-center
items-center
bg-gray-200
text-gray-500
font-semibold
text-base
"
>
{{ i + 1 }}
</div>
<div>
<p class="font-medium">
{{ file.companyName }}
</p>
<p class="text-sm text-gray-600">
{{ file.modified }}
</p>
</div>
</div>
</div>
<hr v-if="files?.length" />
<!-- Language Selector -->
<div
class="w-full flex justify-end absolute px-6 py-6"
style="top: 100%; transform: translateY(-100%)"
@ -177,27 +151,20 @@ export default {
return {
loadingDatabase: false,
fileSelectedFrom: null,
showFiles: false,
files: [],
};
},
mounted() {
this.setFiles();
this.showFiles = this.files.length > 0;
},
watch: {
showFiles() {
this.setFiles();
},
window.ds = this;
},
methods: {
setFiles() {
this.files = cloneDeep(fyo.config.get('files', [])).filter(
({ filePath }) => fs.existsSync(filePath)
);
const files = cloneDeep(fyo.config.get('files', []));
this.files = files.filter(({ dbPath }) => fs.existsSync(dbPath));
for (const file of this.files) {
const stats = fs.statSync(file.filePath);
const stats = fs.statSync(file.dbPath);
file.modified = DateTime.fromJSDate(stats.mtime).toRelative();
}
},
@ -223,7 +190,7 @@ export default {
},
async selectFile(file) {
this.fileSelectedFrom = file;
await this.connectToDatabase(file.filePath);
await this.connectToDatabase(file.dbPath);
},
async connectToDatabase(filePath, isNew) {
if (!filePath) {

View File

@ -112,7 +112,7 @@ export default {
switch (key) {
case 'Opening Balances':
await this.updateChecks({ openingBalanceChecked: 1 });
await this.updateChecks({ openingBalanceChecked: true });
break;
}
},
@ -124,19 +124,19 @@ export default {
switch (key) {
case 'Invoice':
await this.updateChecks({ invoiceSetup: 1 });
await this.updateChecks({ invoiceSetup: true });
break;
case 'General':
await this.updateChecks({ companySetup: 1 });
await this.updateChecks({ companySetup: true });
break;
case 'System':
await this.updateChecks({ systemSetup: 1 });
await this.updateChecks({ systemSetup: true });
break;
case 'Review Accounts':
await this.updateChecks({ chartOfAccountsReviewed: 1 });
await this.updateChecks({ chartOfAccountsReviewed: true });
break;
case 'Add Taxes':
await this.updateChecks({ taxesAdded: 1 });
await this.updateChecks({ taxesAdded: true });
break;
}
},
@ -154,7 +154,7 @@ export default {
if (onboardingComplete) {
await this.updateChecks({ onboardingComplete });
const systemSettings = await fyo.doc.getSingle('SystemSettings');
await systemSettings.set({ hideGetStarted: 1 });
await systemSettings.set('hideGetStarted', true);
await systemSettings.sync();
}
@ -207,9 +207,8 @@ export default {
await this.updateChecks(toUpdate);
},
async updateChecks(toUpdate) {
await fyo.singles.GetStarted.setMultiple(toUpdate);
await fyo.singles.GetStarted.sync();
fyo.singles.GetStarted = await fyo.doc.getSingle('GetStarted');
await fyo.singles.GetStarted.setAndSync(toUpdate);
await fyo.doc.getSingle('GetStarted');
},
isCompleted(item) {
return fyo.singles.GetStarted.get(item.fieldname) || 0;

View File

@ -2,9 +2,7 @@
<div class="mx-4 pb-16 text-base flex flex-col overflow-y-hidden">
<!-- Title Row -->
<div class="flex">
<div class="py-4 mr-3 w-7 border-b" v-if="hasImage">
<p class="text-gray-700">Img</p>
</div>
<div class="py-4 mr-3 w-7" v-if="hasImage" />
<Row
class="flex-1 text-gray-700"
:columnCount="columns.length"
@ -25,11 +23,7 @@
<!-- Data Rows -->
<div class="overflow-y-auto" v-if="data.length !== 0">
<div
class="px-3 flex hover:bg-gray-100 rounded-md"
v-for="doc in data"
:key="doc.name"
>
<div class="flex hover:bg-gray-100" v-for="doc in data" :key="doc.name">
<div class="w-7 py-4 mr-3" v-if="hasImage">
<Avatar :imageURL="doc.image" :label="doc.name" />
</div>

View File

@ -1,6 +1,8 @@
<template>
<div class="border-l h-full">
<!-- Quick edit Tool bar -->
<div class="flex items-center justify-between px-4 pt-4">
<!-- Close Button and Status Text -->
<div class="flex items-center">
<Button :icon="true" @click="routeToPrevious">
<feather-icon name="x" class="w-4 h-4" />
@ -9,6 +11,8 @@
statusText
}}</span>
</div>
<!-- Actions, Badge and Status Change Buttons -->
<div class="flex items-stretch">
<DropdownWithActions :actions="actions" />
<StatusBadge :status="status" />
@ -16,7 +20,7 @@
:icon="true"
@click="sync"
type="primary"
v-if="doc && doc.notInserted"
v-if="doc?.dirty || doc?.notInserted"
class="ml-2 text-white text-xs"
>
{{ t`Save` }}
@ -27,10 +31,9 @@
type="primary"
v-if="
schema?.isSubmittable &&
doc &&
!doc.submitted &&
!doc.notInserted &&
!(doc.cancelled || false)
!doc?.submitted &&
!doc?.notInserted &&
!(doc?.cancelled || false)
"
class="ml-2 text-white text-xs"
>
@ -38,6 +41,8 @@
</Button>
</div>
</div>
<!-- Name and image -->
<div class="px-4 pt-2 pb-4 flex-center" v-if="doc">
<div class="flex flex-col items-center">
<FormControl
@ -63,15 +68,19 @@
/>
</div>
</div>
<!-- Rest of the form -->
<TwoColumnForm
ref="form"
v-if="doc"
:doc="doc"
:fields="fields"
:autosave="true"
:autosave="false"
:column-ratio="[1.1, 2]"
/>
<component v-if="doc && quickEditWidget" :is="quickEditWidget" />
<!-- QuickEdit Widgets -->
<component v-if="quickEditWidget" :is="quickEditWidget" />
</div>
</template>
@ -167,6 +176,10 @@ export default {
return getActionsForDocument(this.doc);
},
quickEditWidget() {
if (this.doc?.notInserted ?? true) {
return null;
}
const widget = getQuickEditWidget(this.schemaName);
if (widget === null) {
return null;
@ -212,7 +225,7 @@ export default {
this.doc.set(fieldname, '');
setTimeout(() => {
this.$refs.titleControl.focus();
this.$refs.titleControl?.focus();
}, 300);
},
async fetchDoc() {
@ -225,9 +238,11 @@ export default {
name: this.doc.name,
});
});
this.doc.on('beforeSync', () => {
this.statusText = t`Saving...`;
});
this.doc.on('afterSync', () => {
setTimeout(() => {
this.statusText = null;
@ -252,6 +267,9 @@ export default {
}
},
routeToPrevious() {
if (this.doc.dirty && !this.doc.notInserted) {
this.doc.load();
}
this.$router.back();
},
setTitleSize() {
@ -261,13 +279,8 @@ export default {
const input = this.$refs.titleControl.getInput();
const value = input.value;
let valueLength = (value || '').length + 1;
if (valueLength < 7) {
valueLength = 7;
}
input.size = valueLength;
const valueLength = (value || '').length + 1;
input.size = Math.max(valueLength, 15);
},
},
};