mirror of
https://github.com/frappe/books.git
synced 2024-11-09 23:30:56 +00:00
fix: get Invoice settings to render and work
- remove "computed" - fix a few formcontrols - simplify doc.ts a bit more
This commit is contained in:
parent
9fbf4fa23c
commit
024687c1b9
@ -75,7 +75,7 @@ export default class DatabaseCore extends DatabaseBase {
|
||||
const db = new DatabaseCore(dbPath);
|
||||
db.connect();
|
||||
|
||||
let query: { countryCode: string }[] = [];
|
||||
let query: { value: string }[] = [];
|
||||
try {
|
||||
query = await db.knex!('SingleValue').where({
|
||||
fieldname: 'countryCode',
|
||||
@ -86,7 +86,7 @@ export default class DatabaseCore extends DatabaseBase {
|
||||
}
|
||||
|
||||
if (query.length > 0) {
|
||||
countryCode = query[0].countryCode as string;
|
||||
countryCode = query[0].value as string;
|
||||
}
|
||||
|
||||
await db.close();
|
||||
|
@ -57,7 +57,6 @@ const SalesInvoiceItem = {
|
||||
fieldname: 'amount',
|
||||
label: 'Amount',
|
||||
fieldtype: 'Float',
|
||||
computed: true,
|
||||
readOnly: true,
|
||||
},
|
||||
],
|
||||
@ -108,7 +107,6 @@ const SalesInvoice = {
|
||||
fieldname: 'grandTotal',
|
||||
label: 'Grand Total',
|
||||
fieldtype: 'Currency',
|
||||
computed: true,
|
||||
readOnly: true,
|
||||
},
|
||||
],
|
||||
|
@ -18,6 +18,7 @@ import {
|
||||
TargetField,
|
||||
} from 'schemas/types';
|
||||
import { getIsNullOrUndef, getMapFromList, getRandomString } from 'utils';
|
||||
import { markRaw } from 'vue';
|
||||
import { isPesa } from '../utils/index';
|
||||
import {
|
||||
areDocValuesEqual,
|
||||
@ -71,7 +72,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
|
||||
constructor(schema: Schema, data: DocValueMap, fyo: Fyo) {
|
||||
super();
|
||||
this.fyo = fyo;
|
||||
this.fyo = markRaw(fyo);
|
||||
this.schema = schema;
|
||||
this.fieldMap = getMapFromList(schema.fields, 'fieldname');
|
||||
|
||||
@ -87,7 +88,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
return this.schema.name;
|
||||
}
|
||||
|
||||
get isNew(): boolean {
|
||||
get notInserted(): boolean {
|
||||
return this._notInserted;
|
||||
}
|
||||
|
||||
@ -176,7 +177,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
}
|
||||
|
||||
_canSet(fieldname: string, value?: DocValue | Doc[]): boolean {
|
||||
if (fieldname === 'numberSeries' && !this._notInserted) {
|
||||
if (fieldname === 'numberSeries' && !this.notInserted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -378,11 +379,13 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
}
|
||||
|
||||
if (data && data.name) {
|
||||
this.syncValues(data);
|
||||
this._syncValues(data);
|
||||
await this.loadLinks();
|
||||
} else {
|
||||
throw new NotFoundError(`Not Found: ${this.schemaName} ${this.name}`);
|
||||
}
|
||||
|
||||
this._notInserted = false;
|
||||
}
|
||||
|
||||
async loadLinks() {
|
||||
@ -420,8 +423,8 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
return link;
|
||||
}
|
||||
|
||||
syncValues(data: DocValueMap) {
|
||||
this.clearValues();
|
||||
_syncValues(data: DocValueMap) {
|
||||
this._clearValues();
|
||||
this._setValuesWithoutChecks(data);
|
||||
this._dirty = false;
|
||||
this.trigger('change', {
|
||||
@ -429,7 +432,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
});
|
||||
}
|
||||
|
||||
clearValues() {
|
||||
_clearValues() {
|
||||
for (const { fieldname } of this.schema.fields) {
|
||||
this[fieldname] = null;
|
||||
}
|
||||
@ -453,19 +456,19 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
}
|
||||
|
||||
async _compareWithCurrentDoc() {
|
||||
if (this.isNew || !this.name || this.schema.isSingle) {
|
||||
if (this.notInserted || !this.name || this.schema.isSingle) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dbValues = await this.fyo.db.get(this.schemaName, this.name);
|
||||
const docModified = this.modified as Date;
|
||||
const dbModified = dbValues.modified as Date;
|
||||
const docModified = (this.modified as Date)?.toISOString();
|
||||
const dbModified = (dbValues.modified as Date)?.toISOString();
|
||||
|
||||
if (dbValues && docModified !== dbModified) {
|
||||
throw new ConflictError(
|
||||
this.fyo
|
||||
.t`Document ${this.schemaName} ${this.name} has been modified after loading` +
|
||||
`${docModified?.toISOString()}, ${dbModified?.toISOString()}`
|
||||
` ${dbModified}, ${docModified}`
|
||||
);
|
||||
}
|
||||
|
||||
@ -575,20 +578,15 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
this._setBaseMetaValues();
|
||||
await this._commit();
|
||||
await this._validateInsert();
|
||||
await this.trigger('beforeInsert', null);
|
||||
|
||||
const oldName = this.name!;
|
||||
const validDict = this.getValidDict();
|
||||
const data = await this.fyo.db.insert(this.schemaName, validDict);
|
||||
this.syncValues(data);
|
||||
this._notInserted = false;
|
||||
this._syncValues(data);
|
||||
|
||||
if (oldName !== this.name) {
|
||||
this.fyo.doc.removeFromCache(this.schemaName, oldName);
|
||||
}
|
||||
|
||||
await this.trigger('afterInsert', null);
|
||||
|
||||
this.fyo.telemetry.log(Verb.Created, this.schemaName);
|
||||
return this;
|
||||
}
|
||||
@ -596,7 +594,6 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
async _update() {
|
||||
await this._compareWithCurrentDoc();
|
||||
await this._commit();
|
||||
await this.trigger('beforeUpdate');
|
||||
|
||||
// before submit
|
||||
if (this.flags.submitAction) await this.trigger('beforeSubmit');
|
||||
@ -607,9 +604,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
|
||||
const data = this.getValidDict();
|
||||
await this.fyo.db.update(this.schemaName, data);
|
||||
this.syncValues(data);
|
||||
|
||||
await this.trigger('afterUpdate');
|
||||
this._syncValues(data);
|
||||
|
||||
// after submit
|
||||
if (this.flags.submitAction) await this.trigger('afterSubmit');
|
||||
@ -621,11 +616,12 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
async sync() {
|
||||
await this.trigger('beforeSync');
|
||||
let doc;
|
||||
if (this._notInserted) {
|
||||
if (this.notInserted) {
|
||||
doc = await this._insert();
|
||||
} else {
|
||||
doc = await this._update();
|
||||
}
|
||||
this._notInserted = false;
|
||||
await this.trigger('afterSync');
|
||||
return doc;
|
||||
}
|
||||
@ -748,14 +744,12 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
return doc;
|
||||
}
|
||||
|
||||
async beforeInsert() {}
|
||||
async afterInsert() {}
|
||||
async beforeUpdate() {}
|
||||
async afterUpdate() {}
|
||||
async beforeSync() {}
|
||||
async afterSync() {}
|
||||
async beforeDelete() {}
|
||||
async afterDelete() {}
|
||||
async beforeSubmit() {}
|
||||
async afterSubmit() {}
|
||||
async beforeRevert() {}
|
||||
async afterRevert() {}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { Fyo } from 'fyo';
|
||||
import { DocValue, DocValueMap } from 'fyo/core/types';
|
||||
import SystemSettings from 'fyo/models/SystemSettings';
|
||||
import { FieldType } from 'schemas/types';
|
||||
@ -50,10 +49,10 @@ export interface SinglesMap {
|
||||
|
||||
// Static Config properties
|
||||
|
||||
export type FilterFunction = (doc: Doc) => QueryFilter;
|
||||
export type FilterFunction = (doc: Doc) => QueryFilter | Promise<QueryFilter>;
|
||||
export type FiltersMap = Record<string, FilterFunction>;
|
||||
|
||||
export type EmptyMessageFunction = (doc: Doc, fyo: Fyo) => string;
|
||||
export type EmptyMessageFunction = (doc: Doc) => string;
|
||||
export type EmptyMessageMap = Record<string, EmptyMessageFunction>;
|
||||
|
||||
export type ListFunction = (doc?: Doc) => string[];
|
||||
|
@ -24,7 +24,7 @@ export class Account extends Doc {
|
||||
rgt: () => 0,
|
||||
};
|
||||
|
||||
async beforeInsert() {
|
||||
async beforeSync() {
|
||||
if (this.accountType || !this.parentAccount) {
|
||||
return;
|
||||
}
|
||||
|
@ -1,6 +1,11 @@
|
||||
import { Fyo } from 'fyo';
|
||||
import { t } from 'fyo';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import { EmptyMessageMap, FormulaMap, ListsMap } from 'fyo/model/types';
|
||||
import {
|
||||
DependsOnMap,
|
||||
EmptyMessageMap,
|
||||
FormulaMap,
|
||||
ListsMap,
|
||||
} from 'fyo/model/types';
|
||||
import { stateCodeMap } from 'regional/in';
|
||||
import { titleCase } from 'utils';
|
||||
import { getCountryInfo } from 'utils/misc';
|
||||
@ -21,6 +26,17 @@ export class Address extends Doc {
|
||||
},
|
||||
};
|
||||
|
||||
dependsOn: DependsOnMap = {
|
||||
addressDisplay: [
|
||||
'addressLine1',
|
||||
'addressLine2',
|
||||
'city',
|
||||
'state',
|
||||
'country',
|
||||
'postalCode',
|
||||
],
|
||||
};
|
||||
|
||||
static lists: ListsMap = {
|
||||
state(doc?: Doc) {
|
||||
const country = doc?.country as string | undefined;
|
||||
@ -37,12 +53,12 @@ export class Address extends Doc {
|
||||
};
|
||||
|
||||
static emptyMessages: EmptyMessageMap = {
|
||||
state: (doc: Doc, fyo: Fyo) => {
|
||||
state: (doc: Doc) => {
|
||||
if (doc.country) {
|
||||
return fyo.t`Enter State`;
|
||||
return t`Enter State`;
|
||||
}
|
||||
|
||||
return fyo.t`Enter Country to load States`;
|
||||
return t`Enter Country to load States`;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -43,12 +43,7 @@ export abstract class Invoice extends Doc {
|
||||
return [];
|
||||
}
|
||||
|
||||
async beforeUpdate() {
|
||||
const entries = await this.getPosting();
|
||||
await entries.validateEntries();
|
||||
}
|
||||
|
||||
async beforeInsert() {
|
||||
async beforeSync() {
|
||||
const entries = await this.getPosting();
|
||||
await entries.validateEntries();
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ export class Item extends Doc {
|
||||
return [
|
||||
{
|
||||
label: fyo.t`New Invoice`,
|
||||
condition: (doc) => !doc.isNew,
|
||||
condition: (doc) => !doc.notInserted,
|
||||
action: async (doc, router) => {
|
||||
const invoice = await fyo.doc.getNewDoc('SalesInvoice');
|
||||
await invoice.append('items', {
|
||||
@ -79,7 +79,7 @@ export class Item extends Doc {
|
||||
},
|
||||
{
|
||||
label: fyo.t`New Bill`,
|
||||
condition: (doc) => !doc.isNew,
|
||||
condition: (doc) => !doc.notInserted,
|
||||
action: async (doc, router) => {
|
||||
const invoice = await fyo.doc.getNewDoc('PurchaseInvoice');
|
||||
await invoice.append('items', {
|
||||
|
@ -32,11 +32,7 @@ export class JournalEntry extends Doc {
|
||||
return entries;
|
||||
}
|
||||
|
||||
async beforeUpdate() {
|
||||
this.getPosting().validateEntries();
|
||||
}
|
||||
|
||||
async beforeInsert() {
|
||||
async beforeSync() {
|
||||
this.getPosting().validateEntries();
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ export class Party extends Doc {
|
||||
{
|
||||
label: fyo.t`Create Bill`,
|
||||
condition: (doc: Doc) =>
|
||||
!doc.isNew && (doc.role as PartyRole) !== 'Customer',
|
||||
!doc.notInserted && (doc.role as PartyRole) !== 'Customer',
|
||||
action: async (partyDoc, router) => {
|
||||
const doc = await fyo.doc.getNewDoc('PurchaseInvoice');
|
||||
router.push({
|
||||
@ -127,7 +127,7 @@ export class Party extends Doc {
|
||||
{
|
||||
label: fyo.t`View Bills`,
|
||||
condition: (doc: Doc) =>
|
||||
!doc.isNew && (doc.role as PartyRole) !== 'Customer',
|
||||
!doc.notInserted && (doc.role as PartyRole) !== 'Customer',
|
||||
action: async (partyDoc, router) => {
|
||||
router.push({
|
||||
name: 'ListView',
|
||||
@ -144,7 +144,7 @@ export class Party extends Doc {
|
||||
{
|
||||
label: fyo.t`Create Invoice`,
|
||||
condition: (doc: Doc) =>
|
||||
!doc.isNew && (doc.role as PartyRole) !== 'Supplier',
|
||||
!doc.notInserted && (doc.role as PartyRole) !== 'Supplier',
|
||||
action: async (partyDoc, router) => {
|
||||
const doc = await fyo.doc.getNewDoc('SalesInvoice');
|
||||
router.push({
|
||||
@ -162,7 +162,7 @@ export class Party extends Doc {
|
||||
{
|
||||
label: fyo.t`View Invoices`,
|
||||
condition: (doc: Doc) =>
|
||||
!doc.isNew && (doc.role as PartyRole) !== 'Supplier',
|
||||
!doc.notInserted && (doc.role as PartyRole) !== 'Supplier',
|
||||
action: async (partyDoc, router) => {
|
||||
router.push({
|
||||
name: 'ListView',
|
||||
|
@ -35,7 +35,7 @@ export function getTransactionActions(schemaName: string, fyo: Fyo): Action[] {
|
||||
(doc.submitted as boolean) && (doc.outstandingAmount as Money).gt(0),
|
||||
action: async function makePayment(doc: Doc) {
|
||||
const payment = await fyo.doc.getNewDoc('Payment');
|
||||
payment.once('afterInsert', async () => {
|
||||
payment.once('afterSync', async () => {
|
||||
await payment.submit();
|
||||
});
|
||||
const isSales = schemaName === 'SalesInvoice';
|
||||
|
@ -3,7 +3,7 @@ import { Party as BaseParty } from 'models/baseModels/Party/Party';
|
||||
import { GSTType } from './types';
|
||||
|
||||
export class Party extends BaseParty {
|
||||
async beforeInsert() {
|
||||
async beforeSync() {
|
||||
const gstin = this.get('gstin') as string | undefined;
|
||||
const gstType = this.get('gstType') as GSTType;
|
||||
|
||||
|
@ -55,14 +55,12 @@
|
||||
"fieldname": "netTotal",
|
||||
"label": "Net Total",
|
||||
"fieldtype": "Currency",
|
||||
"computed": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "baseNetTotal",
|
||||
"label": "Net Total (Company Currency)",
|
||||
"fieldtype": "Currency",
|
||||
"computed": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
@ -76,14 +74,12 @@
|
||||
"fieldname": "grandTotal",
|
||||
"label": "Grand Total",
|
||||
"fieldtype": "Currency",
|
||||
"computed": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "baseGrandTotal",
|
||||
"label": "Grand Total (Company Currency)",
|
||||
"fieldtype": "Currency",
|
||||
"computed": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
|
@ -33,7 +33,6 @@
|
||||
"fieldname": "baseRate",
|
||||
"label": "Rate (Company Currency)",
|
||||
"fieldtype": "Currency",
|
||||
"computed": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
@ -54,14 +53,12 @@
|
||||
"fieldname": "amount",
|
||||
"label": "Amount",
|
||||
"fieldtype": "Currency",
|
||||
"computed": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "baseAmount",
|
||||
"label": "Amount (Company Currency)",
|
||||
"fieldtype": "Currency",
|
||||
"computed": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
|
@ -55,14 +55,12 @@
|
||||
"fieldname": "netTotal",
|
||||
"label": "Net Total",
|
||||
"fieldtype": "Currency",
|
||||
"computed": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "baseNetTotal",
|
||||
"label": "Net Total (Company Currency)",
|
||||
"fieldtype": "Currency",
|
||||
"computed": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
@ -76,21 +74,18 @@
|
||||
"fieldname": "grandTotal",
|
||||
"label": "Grand Total",
|
||||
"fieldtype": "Currency",
|
||||
"computed": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "baseGrandTotal",
|
||||
"label": "Grand Total (Company Currency)",
|
||||
"fieldtype": "Currency",
|
||||
"computed": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "outstandingAmount",
|
||||
"label": "Outstanding Amount",
|
||||
"fieldtype": "Currency",
|
||||
"computed": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
|
@ -33,7 +33,6 @@
|
||||
"fieldname": "baseRate",
|
||||
"label": "Rate (Company Currency)",
|
||||
"fieldtype": "Currency",
|
||||
"computed": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
@ -55,14 +54,12 @@
|
||||
"fieldname": "amount",
|
||||
"label": "Amount",
|
||||
"fieldtype": "Currency",
|
||||
"computed": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"fieldname": "baseAmount",
|
||||
"label": "Amount (Company Currency)",
|
||||
"fieldtype": "Currency",
|
||||
"computed": true,
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
|
@ -19,11 +19,20 @@ export function getSchemas(countryCode: string = '-'): Readonly<SchemaMap> {
|
||||
let schemaMap = Object.assign({}, builtAppSchemas, builtCoreSchemas);
|
||||
schemaMap = addMetaFields(schemaMap);
|
||||
schemaMap = removeFields(schemaMap);
|
||||
schemaMap = setSchemaNameOnFields(schemaMap);
|
||||
|
||||
deepFreeze(schemaMap);
|
||||
return schemaMap;
|
||||
}
|
||||
|
||||
export function setSchemaNameOnFields(schemaMap: SchemaMap): SchemaMap {
|
||||
for (const schemaName in schemaMap) {
|
||||
const schema = schemaMap[schemaName]!;
|
||||
schema.fields = schema.fields.map((f) => ({ ...f, schemaName }));
|
||||
}
|
||||
return schemaMap;
|
||||
}
|
||||
|
||||
function removeFields(schemaMap: SchemaMap): SchemaMap {
|
||||
for (const schemaName in schemaMap) {
|
||||
const schema = schemaMap[schemaName]!;
|
||||
@ -96,9 +105,16 @@ export function addMetaFields(schemaMap: SchemaMap): SchemaMap {
|
||||
}
|
||||
|
||||
addNameField(schemaMap);
|
||||
addTitleField(schemaMap);
|
||||
return schemaMap;
|
||||
}
|
||||
|
||||
function addTitleField(schemaMap: SchemaMap) {
|
||||
for (const schemaName in schemaMap) {
|
||||
schemaMap[schemaName]!.titleField ??= 'name';
|
||||
}
|
||||
}
|
||||
|
||||
function addNameField(schemaMap: SchemaMap) {
|
||||
for (const name in schemaMap) {
|
||||
const schema = schemaMap[name] as Schema;
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
cleanSchemas,
|
||||
getAbstractCombinedSchemas,
|
||||
getRegionalCombinedSchemas,
|
||||
setSchemaNameOnFields,
|
||||
} from '../index';
|
||||
import { metaSchemas } from '../schemas';
|
||||
import {
|
||||
@ -134,7 +135,9 @@ describe('Schema Builder', function () {
|
||||
});
|
||||
});
|
||||
|
||||
const finalSchemas = addMetaFields(cloneDeep(abstractCombined));
|
||||
let almostFinalSchemas = cloneDeep(abstractCombined);
|
||||
almostFinalSchemas = addMetaFields(almostFinalSchemas);
|
||||
const finalSchemas = setSchemaNameOnFields(almostFinalSchemas);
|
||||
const metaSchemaMap = getMapFromList(metaSchemas, 'name');
|
||||
const baseFieldNames = metaSchemaMap.base.fields!.map((f) => f.fieldname);
|
||||
const childFieldNames = metaSchemaMap.child.fields!.map((f) => f.fieldname);
|
||||
@ -150,6 +153,14 @@ describe('Schema Builder', function () {
|
||||
];
|
||||
|
||||
describe('Final Schemas', function () {
|
||||
specify('Schema Name Existsance', function () {
|
||||
for (const schemaName in finalSchemas) {
|
||||
for (const field of finalSchemas[schemaName]?.fields!) {
|
||||
assert.strictEqual(field.schemaName, schemaName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
specify('Schema Field Existance', function () {
|
||||
assert.strictEqual(
|
||||
everyFieldExists(baseFieldNames, finalSchemas.Customer!),
|
||||
|
@ -23,6 +23,7 @@ export interface BaseField {
|
||||
fieldname: string; // Column name in the db
|
||||
fieldtype: FieldType; // UI Descriptive field types that map to column types
|
||||
label: string; // Translateable UI facing name
|
||||
schemaName?: string; // Convenient access to schemaName incase just the field is passed
|
||||
required?: boolean; // Implies Not Null
|
||||
hidden?: boolean; // UI Facing config, whether field is shown in a form
|
||||
readOnly?: boolean; // UI Facing config, whether field is editable
|
||||
@ -30,7 +31,6 @@ export interface BaseField {
|
||||
default?: RawValue; // Default value of a field, should match the db type
|
||||
placeholder?: string; // UI Facing config, form field placeholder
|
||||
groupBy?: string; // UI Facing used in dropdowns fields
|
||||
computed?: boolean; // Indicates whether a value is computed, implies readonly
|
||||
meta?: boolean; // Field is a meta field, i.e. only for the db, not UI
|
||||
inline?: boolean; // UI Facing config, whether to display doc inline.
|
||||
}
|
||||
@ -84,6 +84,7 @@ export interface Schema {
|
||||
quickEditFields?: string[]; // Used to get fields for the quickEditForm
|
||||
inlineEditDisplayField?:string;// Display field if inline editable
|
||||
naming?: Naming; // Used for assigning name, default is 'random' else 'numberSeries' if present
|
||||
titleField?: string; // Main display field
|
||||
removeFields?: string[]; // Used by the builder to remove fields.
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,8 @@ export default {
|
||||
if (hideGetStarted || onboardingComplete) {
|
||||
routeTo('/');
|
||||
} else {
|
||||
routeTo('/get-started');
|
||||
// routeTo('/get-started');
|
||||
routeTo('/settings');
|
||||
}
|
||||
},
|
||||
async changeDbFile() {
|
||||
|
@ -83,9 +83,9 @@ export default {
|
||||
this.isLoading = false;
|
||||
},
|
||||
async getSuggestions(keyword = '') {
|
||||
keyword = keyword.toLowerCase();
|
||||
const options = getOptionList(this.df, this.doc);
|
||||
|
||||
keyword = keyword.toLowerCase();
|
||||
if (!keyword) {
|
||||
return options;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
</div>
|
||||
<div style="width: 14px; height: 14px; overflow: hidden; cursor: pointer">
|
||||
<svg
|
||||
v-if="checked === 1"
|
||||
v-if="checked"
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
@ -57,7 +57,7 @@
|
||||
:class="inputClasses"
|
||||
:checked="value"
|
||||
:readonly="isReadOnly"
|
||||
@change="(e) => triggerChange(+e.target.checked)"
|
||||
@change="(e) => triggerChange(e.target.checked)"
|
||||
@focus="(e) => $emit('focus', e)"
|
||||
/>
|
||||
</div>
|
||||
|
@ -78,10 +78,10 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
colors() {
|
||||
return this.df.colors;
|
||||
return this.df.options;
|
||||
},
|
||||
selectedColorLabel() {
|
||||
let color = this.colors.find((c) => this.value === c.value);
|
||||
const color = this.colors.find((c) => this.value === c.value);
|
||||
return color ? color.label : this.value;
|
||||
},
|
||||
},
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
<script>
|
||||
import { fyo } from 'src/initFyo';
|
||||
import Float from './Float';
|
||||
import Float from './Float.vue';
|
||||
|
||||
export default {
|
||||
name: 'Currency',
|
||||
|
@ -1,11 +1,11 @@
|
||||
<script>
|
||||
import Link from './Link';
|
||||
import Link from './Link.vue';
|
||||
export default {
|
||||
name: 'DynamicLink',
|
||||
props: ['target'],
|
||||
extends: Link,
|
||||
created() {
|
||||
let watchKey = `doc.${this.df.references}`;
|
||||
const watchKey = `doc.${this.df.references}`;
|
||||
this.targetWatcher = this.$watch(watchKey, function(newTarget, oldTarget) {
|
||||
if (oldTarget && newTarget !== oldTarget) {
|
||||
this.triggerChange('');
|
||||
@ -20,6 +20,7 @@ export default {
|
||||
if (!this.doc) {
|
||||
throw new Error('You must provide `doc` for DynamicLink to work.');
|
||||
}
|
||||
|
||||
return this.doc[this.df.references];
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import Int from './Int';
|
||||
import Int from './Int.vue';
|
||||
|
||||
export default {
|
||||
name: 'Float',
|
||||
@ -11,7 +11,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
parse(value) {
|
||||
let parsedValue = parseFloat(value);
|
||||
const parsedValue = parseFloat(value);
|
||||
return isNaN(parsedValue) ? 0 : parsedValue;
|
||||
},
|
||||
},
|
||||
|
@ -15,27 +15,30 @@ import Select from './Select.vue';
|
||||
import Table from './Table.vue';
|
||||
import Text from './Text.vue';
|
||||
|
||||
const components = {
|
||||
AttachImage,
|
||||
Data,
|
||||
Check,
|
||||
Color,
|
||||
Select,
|
||||
Link,
|
||||
Date,
|
||||
Table,
|
||||
AutoComplete,
|
||||
DynamicLink,
|
||||
Int,
|
||||
Float,
|
||||
Currency,
|
||||
Text,
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'FormControl',
|
||||
render() {
|
||||
let controls = {
|
||||
Data,
|
||||
Select,
|
||||
Link,
|
||||
Date,
|
||||
Table,
|
||||
AutoComplete,
|
||||
Check,
|
||||
AttachImage,
|
||||
DynamicLink,
|
||||
Int,
|
||||
Float,
|
||||
Currency,
|
||||
Text,
|
||||
Color,
|
||||
};
|
||||
let { df } = this.$attrs;
|
||||
return h(controls[df.fieldtype] || Data, {
|
||||
const fieldtype = this.$attrs.df.fieldtype;
|
||||
const component = components[fieldtype] ?? Data;
|
||||
|
||||
return h(component, {
|
||||
...this.$attrs,
|
||||
ref: 'control',
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import Data from './Data';
|
||||
import Data from "./Data.vue";
|
||||
|
||||
export default {
|
||||
name: 'Int',
|
||||
@ -11,7 +11,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
parse(value) {
|
||||
let parsedValue = parseInt(value, 10);
|
||||
const parsedValue = parseInt(value, 10);
|
||||
return isNaN(parsedValue) ? 0 : parsedValue;
|
||||
},
|
||||
},
|
||||
|
@ -1,10 +1,9 @@
|
||||
<script>
|
||||
import { t } from 'fyo';
|
||||
import Badge from 'src/components/Badge';
|
||||
import Badge from 'src/components/Badge.vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { openQuickEdit } from 'src/utils/ui';
|
||||
import { markRaw } from 'vue';
|
||||
import AutoComplete from './AutoComplete';
|
||||
import AutoComplete from './AutoComplete.vue';
|
||||
|
||||
export default {
|
||||
name: 'Link',
|
||||
@ -12,54 +11,51 @@ export default {
|
||||
emits: ['new-doc'],
|
||||
methods: {
|
||||
async getSuggestions(keyword = '') {
|
||||
let doctype = this.getTarget();
|
||||
let meta;
|
||||
try {
|
||||
meta = fyo.getMeta(doctype);
|
||||
} catch (err) {
|
||||
if (err.message.includes('not a registered doctype')) {
|
||||
return [];
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
let filters = await this.getFilters(keyword);
|
||||
const schemaName = this.df.target;
|
||||
const schema = fyo.schemaMap[schemaName];
|
||||
const filters = await this.getFilters(keyword);
|
||||
|
||||
if (keyword && !filters.keywords) {
|
||||
filters.keywords = ['like', keyword];
|
||||
}
|
||||
let results = await fyo.db.getAll({
|
||||
doctype,
|
||||
|
||||
const fields = [
|
||||
...new Set([
|
||||
'name',
|
||||
schema.titleField,
|
||||
this.df.groupBy,
|
||||
...schema.keywordFields,
|
||||
]),
|
||||
].filter(Boolean);
|
||||
|
||||
const results = await fyo.db.getAll(schemaName, {
|
||||
filters,
|
||||
fields: [
|
||||
...new Set([
|
||||
'name',
|
||||
meta.titleField,
|
||||
this.df.groupBy,
|
||||
...meta.getKeywordFields(),
|
||||
]),
|
||||
].filter(Boolean),
|
||||
fields,
|
||||
});
|
||||
let createNewOption = this.getCreateNewOption();
|
||||
let suggestions = results
|
||||
|
||||
const suggestions = results
|
||||
.map((r) => {
|
||||
let option = { label: r[meta.titleField], value: r.name };
|
||||
const option = { label: r[schema.titleField], value: r.name };
|
||||
if (this.df.groupBy) {
|
||||
option.group = r[this.df.groupBy];
|
||||
}
|
||||
return option;
|
||||
})
|
||||
.concat(this.df.disableCreation ? null : createNewOption)
|
||||
.concat(this.getCreateNewOption())
|
||||
.filter(Boolean);
|
||||
|
||||
if (suggestions.length === 0) {
|
||||
return [
|
||||
{
|
||||
component: {
|
||||
template: '<span class="text-gray-600">No results found</span>',
|
||||
},
|
||||
component: markRaw({
|
||||
template:
|
||||
'<span class="text-gray-600">{{ t`No results found` }}</span>',
|
||||
}),
|
||||
action: () => {},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
},
|
||||
getCreateNewOption() {
|
||||
@ -83,14 +79,35 @@ export default {
|
||||
}),
|
||||
};
|
||||
},
|
||||
async getFilters(keyword) {
|
||||
const { getFilters } = this.df;
|
||||
if (!getFilters) {
|
||||
async openNewDoc() {
|
||||
const schemaName = this.df.target;
|
||||
const doc = await fyo.doc.getNewDoc(schemaName);
|
||||
const filters = await this.getFilters();
|
||||
const { openQuickEdit } = await import('src/utils/ui');
|
||||
|
||||
openQuickEdit({
|
||||
schemaName,
|
||||
name: doc.name,
|
||||
defaults: Object.assign({}, filters, {
|
||||
name: this.linkValue,
|
||||
}),
|
||||
});
|
||||
|
||||
doc.once('afterSync', () => {
|
||||
this.$emit('new-doc', doc);
|
||||
this.$router.back();
|
||||
});
|
||||
},
|
||||
async getFilters() {
|
||||
const { schemaName, fieldname } = this.df;
|
||||
const getFilters = fyo.models[schemaName].filters[fieldname];
|
||||
|
||||
if (getFilters === undefined) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (this.doc) {
|
||||
return (await getFilters(keyword, this.doc)) ?? {};
|
||||
return (await getFilters(this.doc)) ?? {};
|
||||
}
|
||||
|
||||
try {
|
||||
@ -99,25 +116,6 @@ export default {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
getTarget() {
|
||||
return this.df.target;
|
||||
},
|
||||
async openNewDoc() {
|
||||
let doctype = this.df.target;
|
||||
let doc = await fyo.doc.getNewDoc(doctype);
|
||||
let filters = await this.getFilters();
|
||||
openQuickEdit({
|
||||
doctype,
|
||||
name: doc.name,
|
||||
defaults: Object.assign({}, filters, {
|
||||
name: this.linkValue,
|
||||
}),
|
||||
});
|
||||
doc.once('afterInsert', () => {
|
||||
this.$emit('new-doc', doc);
|
||||
this.$router.back();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -61,10 +61,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Row from 'src/components/Row';
|
||||
import Row from 'src/components/Row.vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import Base from './Base';
|
||||
import TableRow from './TableRow';
|
||||
import Base from './Base.vue';
|
||||
import TableRow from './TableRow.vue';
|
||||
|
||||
export default {
|
||||
name: 'Table',
|
||||
@ -122,8 +122,8 @@ export default {
|
||||
return [0.3].concat(this.tableFields.map(() => 1));
|
||||
},
|
||||
tableFields() {
|
||||
let meta = fyo.getMeta(this.df.childtype);
|
||||
return meta.tableFields.map((fieldname) => meta.getField(fieldname));
|
||||
const fields = fyo.schemaMap[this.df.target].tableFields ?? [];
|
||||
return fields.map((fieldname) => fyo.getField(this.df.target, fieldname));
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -32,9 +32,9 @@
|
||||
</Row>
|
||||
</template>
|
||||
<script>
|
||||
import Row from 'src/components/Row';
|
||||
import { getErrorMessage } from '../../errorHandling';
|
||||
import FormControl from './FormControl';
|
||||
import Row from 'src/components/Row.vue';
|
||||
import { getErrorMessage } from 'src/utils';
|
||||
import FormControl from './FormControl.vue';
|
||||
|
||||
export default {
|
||||
name: 'TableRow',
|
||||
@ -42,6 +42,7 @@ export default {
|
||||
emits: ['remove'],
|
||||
components: {
|
||||
Row,
|
||||
FormControl,
|
||||
},
|
||||
data: () => ({ hovering: false, errors: {} }),
|
||||
beforeCreate() {
|
||||
|
@ -17,7 +17,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Base from './Base';
|
||||
import Base from './Base.vue';
|
||||
|
||||
export default {
|
||||
name: 'Text',
|
||||
|
@ -74,6 +74,7 @@
|
||||
|
||||
<script>
|
||||
import uniq from 'lodash/uniq';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import Popover from './Popover.vue';
|
||||
|
||||
export default {
|
||||
@ -166,13 +167,20 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
getEmptyMessage() {
|
||||
const { emptyMessage } = this.df ?? {};
|
||||
if (typeof emptyMessage === 'function') {
|
||||
return emptyMessage(this.doc);
|
||||
} else if (emptyMessage) {
|
||||
return emptyMessage;
|
||||
if (this.df === null) {
|
||||
return this.t`Empty`;
|
||||
}
|
||||
return this.t`Empty`;
|
||||
|
||||
const { schemaName, fieldname } = this.df;
|
||||
const emptyMessage = fyo.models[schemaName]?.emptyMessages[fieldname]?.(
|
||||
this.doc
|
||||
);
|
||||
|
||||
if (emptyMessage === undefined || emptyMessage.length === 0) {
|
||||
return this.t`Empty`;
|
||||
}
|
||||
|
||||
return emptyMessage;
|
||||
},
|
||||
selectItem(d) {
|
||||
if (d.action) {
|
||||
|
@ -9,9 +9,6 @@ import { statusColor } from 'src/utils/colors';
|
||||
export default {
|
||||
name: 'StatusBadge',
|
||||
props: ['status'],
|
||||
components: {
|
||||
Badge,
|
||||
},
|
||||
computed: {
|
||||
color() {
|
||||
return statusColor[this.status];
|
||||
|
@ -22,23 +22,23 @@
|
||||
<TwoColumnForm
|
||||
ref="inlineEditForm"
|
||||
:doc="inlineEditDoc"
|
||||
:fields="inlineEditFields"
|
||||
:fields="getInlineEditFields(df)"
|
||||
: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">
|
||||
<div class="flex px-4 pb-2 gap-2">
|
||||
<Button class="w-1/2 text-gray-900" @click="stopInlineEditing">
|
||||
{{ t`Cancel` }}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
class="ml-2 w-1/2 text-white"
|
||||
@click="() => saveInlineEditDoc(df)"
|
||||
class="w-1/2 text-white"
|
||||
@click="saveInlineEditDoc"
|
||||
>
|
||||
{{ df.inlineSaveText || t`Save` }}
|
||||
{{ t`Save` }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@ -90,8 +90,9 @@
|
||||
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 { handleErrorWithDialog } from 'src/errorHandling';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { getErrorMessage } from 'src/utils';
|
||||
import { evaluateHidden, evaluateReadOnly } from 'src/utils/doc';
|
||||
|
||||
export default {
|
||||
@ -114,10 +115,8 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inlineEditField: null,
|
||||
inlineEditDoc: null,
|
||||
inlineEditFields: null,
|
||||
inlineEditDisplayField: null,
|
||||
inlineEditField: null,
|
||||
errors: {},
|
||||
};
|
||||
},
|
||||
@ -162,7 +161,7 @@ export default {
|
||||
);
|
||||
},
|
||||
evaluateReadOnly(df) {
|
||||
if (df.fieldname === 'numberSeries' && !this.doc._notInserted) {
|
||||
if (df.fieldname === 'numberSeries' && !this.doc.notInserted) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -177,7 +176,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
let oldValue = this.doc.get(df.fieldname);
|
||||
const oldValue = this.doc.get(df.fieldname);
|
||||
if (oldValue === value) {
|
||||
return;
|
||||
}
|
||||
@ -188,7 +187,7 @@ export default {
|
||||
}
|
||||
|
||||
// handle rename
|
||||
if (this.autosave && df.fieldname === 'name' && !this.doc.isNew()) {
|
||||
if (this.autosave && df.fieldname === 'name' && !this.doc.notInserted) {
|
||||
return this.doc.rename(value);
|
||||
}
|
||||
|
||||
@ -200,21 +199,19 @@ export default {
|
||||
this.errors[df.fieldname] = getErrorMessage(e, this.doc);
|
||||
});
|
||||
|
||||
if (this.autosave && this.doc._dirty && !this.doc.isNew()) {
|
||||
if (this.autosave && this.doc.dirty) {
|
||||
if (df.fieldtype === 'Table') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.doc.sync();
|
||||
}
|
||||
},
|
||||
sync() {
|
||||
return this.doc.sync().catch(this.handleError);
|
||||
return this.doc.sync().catch((e) => handleErrorWithDialog(e, this.doc));
|
||||
},
|
||||
submit() {
|
||||
return this.doc.submit().catch(this.handleError);
|
||||
},
|
||||
handleError(e) {
|
||||
handleErrorWithDialog(e, this.doc);
|
||||
return this.doc.submit().catch((e) => handleErrorWithDialog(e, this.doc));
|
||||
},
|
||||
async activateInlineEditing(df) {
|
||||
if (!df.inline) {
|
||||
@ -224,28 +221,39 @@ export default {
|
||||
this.inlineEditField = df;
|
||||
if (!this.doc[df.fieldname]) {
|
||||
this.inlineEditDoc = await fyo.doc.getNewDoc(df.target);
|
||||
this.inlineEditDoc.once('afterInsert', () => {
|
||||
this.inlineEditDoc.once('afterSync', () => {
|
||||
this.onChangeCommon(df, this.inlineEditDoc.name);
|
||||
});
|
||||
} else {
|
||||
this.inlineEditDoc = this.doc.getLink(df.fieldname);
|
||||
}
|
||||
|
||||
this.inlineEditDisplayField =
|
||||
this.doc.schema.inlineEditDisplayField ?? 'name';
|
||||
this.inlineEditFields = fyo.schemaMap[df.target].quickEditFields ?? [];
|
||||
},
|
||||
async saveInlineEditDoc(df) {
|
||||
getInlineEditFields(df) {
|
||||
const inlineEditFieldNames =
|
||||
fyo.schemaMap[df.target].quickEditFields ?? [];
|
||||
return inlineEditFieldNames.map((fieldname) =>
|
||||
fyo.getField(df.target, fieldname)
|
||||
);
|
||||
},
|
||||
async saveInlineEditDoc() {
|
||||
if (!this.inlineEditDoc) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.$refs.inlineEditForm[0].sync();
|
||||
await this.doc.loadLinks();
|
||||
|
||||
if (this.emitChange && df.inline) {
|
||||
this.$emit('change', df.inlineEditField);
|
||||
if (this.emitChange) {
|
||||
this.$emit('change', this.inlineEditField);
|
||||
}
|
||||
|
||||
await this.stopInlineEditing();
|
||||
},
|
||||
async stopInlineEditing() {
|
||||
if (this.inlineEditDoc?.dirty) {
|
||||
await this.inlineEditDoc.load()
|
||||
}
|
||||
this.inlineEditDoc = null;
|
||||
this.inlineEditField = null;
|
||||
},
|
||||
},
|
||||
|
@ -3,15 +3,11 @@ import { t } from 'fyo';
|
||||
import { ConfigKeys } from 'fyo/core/types';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import { TelemetrySetting } from 'fyo/telemetry/types';
|
||||
import {
|
||||
DuplicateEntryError,
|
||||
LinkValidationError,
|
||||
MandatoryError,
|
||||
ValidationError,
|
||||
} from 'fyo/utils/errors';
|
||||
import { MandatoryError, ValidationError } from 'fyo/utils/errors';
|
||||
import { ErrorLog } from 'fyo/utils/types';
|
||||
import { IPC_ACTIONS, IPC_MESSAGES } from 'utils/messages';
|
||||
import { fyo } from './initFyo';
|
||||
import { getErrorMessage } from './utils';
|
||||
import { ToastOptions } from './utils/types';
|
||||
import { showMessageDialog, showToast } from './utils/ui';
|
||||
|
||||
@ -106,22 +102,6 @@ export function handleError(
|
||||
}
|
||||
}
|
||||
|
||||
export function getErrorMessage(e: Error, doc?: Doc): string {
|
||||
let errorMessage = e.message || t`An error occurred.`;
|
||||
|
||||
const { schemaName, name }: { schemaName?: string; name?: string } =
|
||||
doc ?? {};
|
||||
const canElaborate = !!(schemaName && name);
|
||||
|
||||
if (e instanceof LinkValidationError && canElaborate) {
|
||||
errorMessage = t`${schemaName} ${name} is linked with existing records.`;
|
||||
} else if (e instanceof DuplicateEntryError && canElaborate) {
|
||||
errorMessage = t`${schemaName} ${name} already exists.`;
|
||||
}
|
||||
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
export function handleErrorWithDialog(error: Error, doc?: Doc) {
|
||||
const errorMessage = getErrorMessage(error, doc);
|
||||
handleError(false, error, { errorMessage, doc });
|
||||
|
@ -72,7 +72,7 @@ export default {
|
||||
}
|
||||
let path = this.getFormPath(doc.name);
|
||||
routeTo(path);
|
||||
doc.on('afterInsert', () => {
|
||||
doc.on('afterSync', () => {
|
||||
let path = this.getFormPath(doc.name);
|
||||
this.$router.replace(path);
|
||||
});
|
||||
|
@ -166,7 +166,7 @@ export default {
|
||||
await this.fetchDoc();
|
||||
|
||||
// setup the title field
|
||||
if (this.doc && this.doc.isNew() && this.doc[this.titleField.fieldname]) {
|
||||
if (this.doc && this.doc.notInserted && this.doc[this.titleField.fieldname]) {
|
||||
if (!this.titleField.readOnly) {
|
||||
this.doc.set(this.titleField.fieldname, '');
|
||||
setTimeout(() => {
|
||||
@ -195,10 +195,10 @@ export default {
|
||||
name: this.doc.name,
|
||||
});
|
||||
});
|
||||
this.doc.on('beforeUpdate', () => {
|
||||
this.doc.on('beforeSync', () => {
|
||||
this.statusText = t`Saving...`;
|
||||
});
|
||||
this.doc.on('afterUpdate', () => {
|
||||
this.doc.on('afterSync', () => {
|
||||
setTimeout(() => {
|
||||
this.statusText = null;
|
||||
}, 500);
|
||||
|
@ -51,16 +51,15 @@
|
||||
<script>
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { t } from 'fyo';
|
||||
import Button from 'src/components/Button';
|
||||
import Icon from 'src/components/Icon';
|
||||
import PageHeader from 'src/components/PageHeader';
|
||||
import Row from 'src/components/Row';
|
||||
import StatusBadge from 'src/components/StatusBadge';
|
||||
import WindowControls from 'src/components/WindowControls';
|
||||
import Button from 'src/components/Button.vue';
|
||||
import Icon from 'src/components/Icon.vue';
|
||||
import PageHeader from 'src/components/PageHeader.vue';
|
||||
import Row from 'src/components/Row.vue';
|
||||
import StatusBadge from 'src/components/StatusBadge.vue';
|
||||
import WindowControls from 'src/components/WindowControls.vue';
|
||||
import { showToast } from 'src/utils/ui';
|
||||
import { IPC_MESSAGES } from 'utils/messages';
|
||||
import { h, markRaw } from 'vue';
|
||||
import TabGeneral from './TabGeneral.vue';
|
||||
import TabInvoice from './TabInvoice.vue';
|
||||
import TabSystem from './TabSystem.vue';
|
||||
|
||||
@ -89,13 +88,15 @@ export default {
|
||||
key: 'General',
|
||||
label: t`General`,
|
||||
icon: 'general',
|
||||
component: markRaw(TabGeneral),
|
||||
// component: markRaw(TabGeneral),
|
||||
component: {template: `<h1>General</h1>`}
|
||||
},
|
||||
{
|
||||
key: 'System',
|
||||
label: t`System`,
|
||||
icon: 'system',
|
||||
component: markRaw(TabSystem),
|
||||
component: {template: `<h1>System</h1>`}
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -6,8 +6,7 @@
|
||||
:value="doc.logo"
|
||||
@change="
|
||||
(value) => {
|
||||
doc.set('logo', value);
|
||||
doc.sync();
|
||||
doc.setAndSync('logo', value);
|
||||
forwardChangeEvent(getField('logo'));
|
||||
}
|
||||
"
|
||||
@ -26,8 +25,7 @@
|
||||
:show-label="true"
|
||||
@change="
|
||||
(value) => {
|
||||
doc.set('displayLogo', value);
|
||||
doc.sync();
|
||||
doc.setAndSync('displayLogo', value);
|
||||
forwardChangeEvent(getField('displayLogo'));
|
||||
}
|
||||
"
|
||||
@ -47,10 +45,10 @@
|
||||
</template>
|
||||
<script>
|
||||
import { ipcRenderer } from 'electron';
|
||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||
import TwoColumnForm from 'src/components/TwoColumnForm';
|
||||
import TwoColumnForm from 'src/components/TwoColumnForm.vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { IPC_ACTIONS } from 'utils/messages';
|
||||
import FormControl from '../../components/Controls/FormControl.vue';
|
||||
|
||||
export default {
|
||||
name: 'TabInvoice',
|
||||
|
@ -95,8 +95,8 @@ import { ipcRenderer } from 'electron';
|
||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||
import LanguageSelector from 'src/components/Controls/LanguageSelector.vue';
|
||||
import TwoColumnForm from 'src/components/TwoColumnForm';
|
||||
import { getErrorMessage } from 'src/errorHandling';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { getErrorMessage } from 'src/utils';
|
||||
import { getSetupWizardDoc } from 'src/utils/misc';
|
||||
import { showMessageDialog } from 'src/utils/ui';
|
||||
import { IPC_MESSAGES } from 'utils/messages';
|
||||
|
@ -9,7 +9,7 @@ import GetStarted from 'src/pages/GetStarted.vue';
|
||||
// import PrintView from 'src/pages/PrintView/PrintView.vue';
|
||||
// import QuickEditForm from 'src/pages/QuickEditForm.vue';
|
||||
// import Report from 'src/pages/Report.vue';
|
||||
// import Settings from 'src/pages/Settings/Settings.vue';
|
||||
import Settings from 'src/pages/Settings/Settings.vue';
|
||||
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
|
||||
import { fyo } from './initFyo';
|
||||
|
||||
@ -108,13 +108,13 @@ const routes: RouteRecordRaw[] = [
|
||||
name: 'Data Import',
|
||||
component: DataImport,
|
||||
},
|
||||
*/
|
||||
{
|
||||
path: '/settings',
|
||||
name: 'Settings',
|
||||
component: Settings,
|
||||
props: true,
|
||||
},
|
||||
*/
|
||||
];
|
||||
|
||||
const router = createRouter({ routes, history: createWebHistory() });
|
||||
|
@ -43,7 +43,8 @@ export function getOptionList(field: Field, doc: Doc): SelectOption[] {
|
||||
}
|
||||
|
||||
function _getOptionList(field: Field, doc: Doc) {
|
||||
if ((field as OptionField).options) {
|
||||
const options = (field as OptionField).options;
|
||||
if (options && options.length > 0) {
|
||||
return (field as OptionField).options;
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
/**
|
||||
* General purpose utils used by the frontend.
|
||||
*/
|
||||
import { t } from 'fyo';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import { isPesa } from 'fyo/utils';
|
||||
import { DuplicateEntryError, LinkValidationError } from 'fyo/utils/errors';
|
||||
import Money from 'pesa/dist/types/src/money';
|
||||
|
||||
export function stringifyCircular(
|
||||
@ -76,3 +78,19 @@ export function convertPesaValuesToFloat(obj: Record<string, unknown>) {
|
||||
obj[key] = (value as Money).float;
|
||||
});
|
||||
}
|
||||
|
||||
export function getErrorMessage(e: Error, doc?: Doc): string {
|
||||
let errorMessage = e.message || t`An error occurred.`;
|
||||
|
||||
const { schemaName, name }: { schemaName?: string; name?: string } =
|
||||
doc ?? {};
|
||||
const canElaborate = !!(schemaName && name);
|
||||
|
||||
if (e instanceof LinkValidationError && canElaborate) {
|
||||
errorMessage = t`${schemaName} ${name} is linked with existing records.`;
|
||||
} else if (e instanceof DuplicateEntryError && canElaborate) {
|
||||
errorMessage = t`${schemaName} ${name} already exists.`;
|
||||
}
|
||||
|
||||
return errorMessage;
|
||||
}
|
@ -28,8 +28,6 @@ export async function openQuickEdit({
|
||||
showFields,
|
||||
defaults = {},
|
||||
}: QuickEditOptions) {
|
||||
const router = (await import('src/router')).default;
|
||||
|
||||
const currentRoute = router.currentRoute.value;
|
||||
const query = currentRoute.query;
|
||||
let method: 'push' | 'replace' = 'push';
|
||||
@ -287,7 +285,10 @@ function getDeleteAction(doc: Doc): Action {
|
||||
template: '<span class="text-red-700">{{ t`Delete` }}</span>',
|
||||
},
|
||||
condition: (doc: Doc) =>
|
||||
!doc.isNew && !doc.submitted && !doc.schema.isSingle && !doc.cancelled,
|
||||
!doc.notInserted &&
|
||||
!doc.submitted &&
|
||||
!doc.schema.isSingle &&
|
||||
!doc.cancelled,
|
||||
action: () =>
|
||||
deleteDocWithPrompt(doc).then((res) => {
|
||||
if (res) {
|
||||
|
@ -23,7 +23,7 @@
|
||||
"fixtures/*": ["fixtures/*"],
|
||||
"reports/*": ["reports/*"],
|
||||
"models/*": ["models/*"],
|
||||
"utils/*": ["utils/*"],
|
||||
"utils/*": ["utils/*"]
|
||||
},
|
||||
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
|
||||
},
|
||||
@ -46,7 +46,7 @@
|
||||
"regional/**/*.ts",
|
||||
"reports/**/*.ts",
|
||||
"utils/**/*.ts",
|
||||
"tests/**/*.ts",
|
||||
"tests/**/*.ts"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user