mirror of
https://github.com/frappe/books.git
synced 2024-12-22 19:09:01 +00:00
incr: search improvements
- get form to render - minor ui fixes
This commit is contained in:
parent
ecfae41635
commit
3e466c647c
@ -31,7 +31,6 @@ import {
|
||||
Action,
|
||||
CurrenciesMap,
|
||||
DefaultMap,
|
||||
DependsOnMap,
|
||||
EmptyMessageMap,
|
||||
FiltersMap,
|
||||
FormulaMap,
|
||||
@ -42,7 +41,7 @@ import {
|
||||
ReadOnlyMap,
|
||||
RequiredMap,
|
||||
TreeViewSettings,
|
||||
ValidationMap
|
||||
ValidationMap,
|
||||
} from './types';
|
||||
import { validateOptions, validateRequired } from './validationFunction';
|
||||
|
||||
@ -583,7 +582,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
}
|
||||
|
||||
async _getValueFromFormula(field: Field, doc: Doc) {
|
||||
const formula = doc.formulas[field.fieldname];
|
||||
const { formula } = doc.formulas[field.fieldname] ?? {};
|
||||
if (formula === undefined) {
|
||||
return;
|
||||
}
|
||||
@ -594,6 +593,7 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(value) && field.fieldtype === FieldTypeEnum.Table) {
|
||||
value = value.map((row) => this._getChildDoc(row, field.fieldname));
|
||||
}
|
||||
@ -796,7 +796,6 @@ export class Doc extends Observable<DocValue | Doc[]> {
|
||||
required: RequiredMap = {};
|
||||
hidden: HiddenMap = {};
|
||||
readOnly: ReadOnlyMap = {};
|
||||
dependsOn: DependsOnMap = {};
|
||||
getCurrencies: CurrenciesMap = {};
|
||||
|
||||
static lists: ListsMap = {};
|
||||
|
@ -92,7 +92,15 @@ export function shouldApplyFormula(field: Field, doc: Doc, fieldname?: string) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const dependsOn = doc.dependsOn[field.fieldname] ?? [];
|
||||
const { dependsOn } = doc.formulas[field.fieldname] ?? {};
|
||||
if (dependsOn === undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dependsOn.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fieldname && dependsOn.includes(fieldname)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -28,7 +28,10 @@ export function isNameAutoSet(schemaName: string, fyo: Fyo): boolean {
|
||||
}
|
||||
|
||||
export async function setName(doc: Doc, fyo: Fyo) {
|
||||
// if is server, always name again if autoincrement or other
|
||||
if (doc.schema.naming === 'manual') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (doc.schema.naming === 'autoincrement') {
|
||||
doc.name = await getNextId(doc.schemaName, fyo);
|
||||
return;
|
||||
|
@ -19,6 +19,7 @@ import { Doc } from './doc';
|
||||
*/
|
||||
export type FormulaReturn = DocValue | DocValueMap[] | undefined | Doc[];
|
||||
export type Formula = () => Promise<FormulaReturn> | FormulaReturn;
|
||||
export type FormulaConfig = { dependsOn?: string[]; formula: Formula };
|
||||
export type Default = () => DocValue;
|
||||
export type Validation = (value: DocValue) => Promise<void> | void;
|
||||
export type Required = () => boolean;
|
||||
@ -26,14 +27,13 @@ export type Hidden = () => boolean;
|
||||
export type ReadOnly = () => boolean;
|
||||
export type GetCurrency = () => string;
|
||||
|
||||
export type FormulaMap = Record<string, Formula | undefined>;
|
||||
export type FormulaMap = Record<string, FormulaConfig | undefined>;
|
||||
export type DefaultMap = Record<string, Default | undefined>;
|
||||
export type ValidationMap = Record<string, Validation | undefined>;
|
||||
export type RequiredMap = Record<string, Required | undefined>;
|
||||
export type CurrenciesMap = Record<string, GetCurrency | undefined>;
|
||||
export type HiddenMap = Record<string, Hidden | undefined>;
|
||||
export type ReadOnlyMap = Record<string, ReadOnly | undefined>;
|
||||
export type DependsOnMap = Record<string, string[]>;
|
||||
|
||||
/**
|
||||
* Should add this for hidden too
|
||||
|
@ -1,31 +1,35 @@
|
||||
export type AccountType =
|
||||
| 'Accumulated Depreciation'
|
||||
| 'Bank'
|
||||
| 'Cash'
|
||||
| 'Chargeable'
|
||||
| 'Cost of Goods Sold'
|
||||
| 'Depreciation'
|
||||
| 'Equity'
|
||||
| 'Expense Account'
|
||||
| 'Expenses Included In Valuation'
|
||||
| 'Fixed Asset'
|
||||
| 'Income Account'
|
||||
| 'Payable'
|
||||
| 'Receivable'
|
||||
| 'Round Off'
|
||||
| 'Stock'
|
||||
| 'Stock Adjustment'
|
||||
| 'Stock Received But Not Billed'
|
||||
| 'Tax'
|
||||
| 'Temporary';
|
||||
export enum AccountRootTypeEnum {
|
||||
'Asset'='Asset',
|
||||
'Liability'='Liability',
|
||||
'Equity'='Equity',
|
||||
'Income'='Income',
|
||||
'Expense'='Expense',
|
||||
}
|
||||
|
||||
export type AccountRootType =
|
||||
| 'Asset'
|
||||
| 'Liability'
|
||||
| 'Equity'
|
||||
| 'Income'
|
||||
| 'Expense';
|
||||
export enum AccountTypeEnum {
|
||||
'Accumulated Depreciation' = 'Accumulated Depreciation',
|
||||
'Bank' = 'Bank',
|
||||
'Cash' = 'Cash',
|
||||
'Chargeable' = 'Chargeable',
|
||||
'Cost of Goods Sold' = 'Cost of Goods Sold',
|
||||
'Depreciation' = 'Depreciation',
|
||||
'Equity' = 'Equity',
|
||||
'Expense Account' = 'Expense Account',
|
||||
'Expenses Included In Valuation' = 'Expenses Included In Valuation',
|
||||
'Fixed Asset' = 'Fixed Asset',
|
||||
'Income Account' = 'Income Account',
|
||||
'Payable' = 'Payable',
|
||||
'Receivable' = 'Receivable',
|
||||
'Round Off' = 'Round Off',
|
||||
'Stock' = 'Stock',
|
||||
'Stock Adjustment' = 'Stock Adjustment',
|
||||
'Stock Received But Not Billed' = 'Stock Received But Not Billed',
|
||||
'Tax' = 'Tax',
|
||||
'Temporary' = 'Temporary',
|
||||
}
|
||||
|
||||
export type AccountType = keyof typeof AccountTypeEnum;
|
||||
export type AccountRootType = keyof typeof AccountRootTypeEnum
|
||||
|
||||
export interface COARootAccount {
|
||||
rootType: AccountRootType;
|
||||
|
@ -1,18 +1,14 @@
|
||||
import { t } from 'fyo';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import {
|
||||
DependsOnMap,
|
||||
EmptyMessageMap,
|
||||
FormulaMap,
|
||||
ListsMap,
|
||||
} from 'fyo/model/types';
|
||||
import { EmptyMessageMap, FormulaMap, ListsMap } from 'fyo/model/types';
|
||||
import { stateCodeMap } from 'regional/in';
|
||||
import { titleCase } from 'utils';
|
||||
import { getCountryInfo } from 'utils/misc';
|
||||
|
||||
export class Address extends Doc {
|
||||
formulas: FormulaMap = {
|
||||
addressDisplay: async () => {
|
||||
addressDisplay: {
|
||||
formula: async () => {
|
||||
return [
|
||||
this.addressLine1,
|
||||
this.addressLine2,
|
||||
@ -24,10 +20,7 @@ export class Address extends Doc {
|
||||
.filter(Boolean)
|
||||
.join(', ');
|
||||
},
|
||||
};
|
||||
|
||||
dependsOn: DependsOnMap = {
|
||||
addressDisplay: [
|
||||
dependsOn: [
|
||||
'addressLine1',
|
||||
'addressLine2',
|
||||
'city',
|
||||
@ -35,6 +28,7 @@ export class Address extends Doc {
|
||||
'country',
|
||||
'postalCode',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
static lists: ListsMap = {
|
||||
|
@ -162,25 +162,36 @@ export abstract class Invoice extends Doc {
|
||||
}
|
||||
|
||||
formulas: FormulaMap = {
|
||||
account: async () =>
|
||||
account: {
|
||||
formula: async () =>
|
||||
this.getFrom('Party', this.party!, 'defaultAccount') as string,
|
||||
currency: async () =>
|
||||
dependsOn: ['party'],
|
||||
},
|
||||
currency: {
|
||||
formula: async () =>
|
||||
(this.getFrom('Party', this.party!, 'currency') as string) ||
|
||||
(this.fyo.singles.AccountingSettings!.currency as string),
|
||||
exchangeRate: async () => await this.getExchangeRate(),
|
||||
netTotal: async () => this.getSum('items', 'amount', false),
|
||||
baseNetTotal: async () => this.netTotal!.mul(this.exchangeRate!),
|
||||
taxes: async () => await this.getTaxSummary(),
|
||||
grandTotal: async () => await this.getGrandTotal(),
|
||||
baseGrandTotal: async () =>
|
||||
(this.grandTotal as Money).mul(this.exchangeRate!),
|
||||
outstandingAmount: async () => {
|
||||
dependsOn: ['party'],
|
||||
},
|
||||
exchangeRate: { formula: async () => await this.getExchangeRate() },
|
||||
netTotal: { formula: async () => this.getSum('items', 'amount', false) },
|
||||
baseNetTotal: {
|
||||
formula: async () => this.netTotal!.mul(this.exchangeRate!),
|
||||
},
|
||||
taxes: { formula: async () => await this.getTaxSummary() },
|
||||
grandTotal: { formula: async () => await this.getGrandTotal() },
|
||||
baseGrandTotal: {
|
||||
formula: async () => (this.grandTotal as Money).mul(this.exchangeRate!),
|
||||
},
|
||||
outstandingAmount: {
|
||||
formula: async () => {
|
||||
if (this.submitted) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.baseGrandTotal!;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static defaults: DefaultMap = {
|
||||
|
@ -1,11 +1,6 @@
|
||||
import { DocValue } from 'fyo/core/types';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import {
|
||||
DependsOnMap,
|
||||
FiltersMap,
|
||||
FormulaMap,
|
||||
ValidationMap,
|
||||
} from 'fyo/model/types';
|
||||
import { FiltersMap, FormulaMap, ValidationMap } from 'fyo/model/types';
|
||||
import { ValidationError } from 'fyo/utils/errors';
|
||||
import Money from 'pesa/dist/types/src/money';
|
||||
import { Invoice } from '../Invoice/Invoice';
|
||||
@ -21,13 +16,17 @@ export abstract class InvoiceItem extends Doc {
|
||||
}
|
||||
|
||||
formulas: FormulaMap = {
|
||||
description: () =>
|
||||
description: {
|
||||
formula: () =>
|
||||
this.parentdoc!.getFrom(
|
||||
'Item',
|
||||
this.item as string,
|
||||
'description'
|
||||
) as string,
|
||||
rate: async () => {
|
||||
dependsOn: ['item'],
|
||||
},
|
||||
rate: {
|
||||
formula: async () => {
|
||||
const baseRate = ((await this.parentdoc!.getFrom(
|
||||
'Item',
|
||||
this.item as string,
|
||||
@ -36,16 +35,29 @@ export abstract class InvoiceItem extends Doc {
|
||||
|
||||
return baseRate.div(this.exchangeRate!);
|
||||
},
|
||||
baseRate: () =>
|
||||
dependsOn: ['item'],
|
||||
},
|
||||
baseRate: {
|
||||
formula: () =>
|
||||
(this.rate as Money).mul(this.parentdoc!.exchangeRate as number),
|
||||
account: () => {
|
||||
dependsOn: ['item', 'rate'],
|
||||
},
|
||||
account: {
|
||||
formula: () => {
|
||||
let accountType = 'expenseAccount';
|
||||
if (this.isSales) {
|
||||
accountType = 'incomeAccount';
|
||||
}
|
||||
return this.parentdoc!.getFrom('Item', this.item as string, accountType);
|
||||
return this.parentdoc!.getFrom(
|
||||
'Item',
|
||||
this.item as string,
|
||||
accountType
|
||||
);
|
||||
},
|
||||
tax: () => {
|
||||
dependsOn: ['item'],
|
||||
},
|
||||
tax: {
|
||||
formula: () => {
|
||||
if (this.tax) {
|
||||
return this.tax as string;
|
||||
}
|
||||
@ -56,15 +68,22 @@ export abstract class InvoiceItem extends Doc {
|
||||
'tax'
|
||||
) as string;
|
||||
},
|
||||
amount: () => (this.rate as Money).mul(this.quantity as number),
|
||||
baseAmount: () =>
|
||||
dependsOn: ['item'],
|
||||
},
|
||||
amount: {
|
||||
formula: () => (this.rate as Money).mul(this.quantity as number),
|
||||
dependsOn: ['item', 'rate', 'quantity'],
|
||||
},
|
||||
baseAmount: {
|
||||
formula: () =>
|
||||
(this.amount as Money).mul(this.parentdoc!.exchangeRate as number),
|
||||
hsnCode: () =>
|
||||
dependsOn: ['item', 'amount', 'rate', 'quantity'],
|
||||
},
|
||||
hsnCode: {
|
||||
formula: () =>
|
||||
this.parentdoc!.getFrom('Item', this.item as string, 'hsnCode'),
|
||||
};
|
||||
|
||||
dependsOn: DependsOnMap = {
|
||||
hsnCode: ['item'],
|
||||
dependsOn: ['item'],
|
||||
},
|
||||
};
|
||||
|
||||
validations: ValidationMap = {
|
||||
|
@ -3,7 +3,6 @@ import { DocValue } from 'fyo/core/types';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import {
|
||||
Action,
|
||||
DependsOnMap,
|
||||
FiltersMap,
|
||||
FormulaMap,
|
||||
ListViewSettings,
|
||||
@ -11,10 +10,12 @@ import {
|
||||
} from 'fyo/model/types';
|
||||
import { ValidationError } from 'fyo/utils/errors';
|
||||
import Money from 'pesa/dist/types/src/money';
|
||||
import { AccountRootTypeEnum, AccountTypeEnum } from '../Account/types';
|
||||
|
||||
export class Item extends Doc {
|
||||
formulas: FormulaMap = {
|
||||
incomeAccount: async () => {
|
||||
incomeAccount: {
|
||||
formula: async () => {
|
||||
let accountName = 'Service';
|
||||
if (this.itemType === 'Product') {
|
||||
accountName = 'Sales';
|
||||
@ -23,10 +24,13 @@ export class Item extends Doc {
|
||||
const accountExists = await this.fyo.db.exists('Account', accountName);
|
||||
return accountExists ? accountName : '';
|
||||
},
|
||||
expenseAccount: async () => {
|
||||
dependsOn: ['itemType'],
|
||||
},
|
||||
expenseAccount: {
|
||||
formula: async () => {
|
||||
const cogs = await this.fyo.db.getAllRaw('Account', {
|
||||
filters: {
|
||||
accountType: 'Cost of Goods Sold',
|
||||
accountType: AccountTypeEnum['Cost of Goods Sold'],
|
||||
},
|
||||
});
|
||||
|
||||
@ -36,24 +40,21 @@ export class Item extends Doc {
|
||||
return cogs[0].name as string;
|
||||
}
|
||||
},
|
||||
dependsOn: ['itemType'],
|
||||
},
|
||||
};
|
||||
|
||||
static filters: FiltersMap = {
|
||||
incomeAccount: () => ({
|
||||
isGroup: false,
|
||||
rootType: 'Income',
|
||||
rootType: AccountRootTypeEnum.Income,
|
||||
}),
|
||||
expenseAccount: () => ({
|
||||
isGroup: false,
|
||||
rootType: 'Expense',
|
||||
rootType: AccountRootTypeEnum.Expense,
|
||||
}),
|
||||
};
|
||||
|
||||
dependsOn: DependsOnMap = {
|
||||
incomeAccount: ['itemType'],
|
||||
expenseAccount: ['itemType'],
|
||||
};
|
||||
|
||||
validations: ValidationMap = {
|
||||
rate: async (value: DocValue) => {
|
||||
if ((value as Money).isNegative()) {
|
||||
|
@ -24,8 +24,14 @@ export class JournalEntryAccount extends Doc {
|
||||
}
|
||||
|
||||
formulas: FormulaMap = {
|
||||
debit: async () => this.getAutoDebitCredit('debit'),
|
||||
credit: async () => this.getAutoDebitCredit('credit'),
|
||||
debit: {
|
||||
formula: async () => this.getAutoDebitCredit('debit'),
|
||||
dependsOn: ['credit'],
|
||||
},
|
||||
credit: {
|
||||
formula: async () => this.getAutoDebitCredit('credit'),
|
||||
dependsOn: ['debit'],
|
||||
},
|
||||
};
|
||||
|
||||
static filters: FiltersMap = {
|
||||
|
@ -51,7 +51,8 @@ export class Party extends Doc {
|
||||
}
|
||||
|
||||
formulas: FormulaMap = {
|
||||
defaultAccount: async () => {
|
||||
defaultAccount: {
|
||||
formula: async () => {
|
||||
const role = this.role as PartyRole;
|
||||
if (role === 'Both') {
|
||||
return '';
|
||||
@ -65,15 +66,21 @@ export class Party extends Doc {
|
||||
const accountExists = await this.fyo.db.exists('Account', accountName);
|
||||
return accountExists ? accountName : '';
|
||||
},
|
||||
currency: async () =>
|
||||
dependsOn: ['role'],
|
||||
},
|
||||
currency: {
|
||||
formula: async () =>
|
||||
this.fyo.singles.AccountingSettings!.currency as string,
|
||||
address: async () => {
|
||||
},
|
||||
address: {
|
||||
formula: async () => {
|
||||
const address = this.address as string | undefined;
|
||||
if (address) {
|
||||
return this.getFrom('Address', address, 'addressDisplay') as string;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
validations: ValidationMap = {
|
||||
|
@ -38,7 +38,8 @@ export class Payment extends Doc {
|
||||
|
||||
async updateDetailsOnReferenceUpdate() {
|
||||
const forReferences = (this.for ?? []) as Doc[];
|
||||
const { referenceType, referenceName } = forReferences[0];
|
||||
|
||||
const { referenceType, referenceName } = forReferences[0] ?? {};
|
||||
if (
|
||||
forReferences.length !== 1 ||
|
||||
this.party ||
|
||||
@ -284,17 +285,26 @@ export class Payment extends Doc {
|
||||
static defaults: DefaultMap = { date: () => new Date().toISOString() };
|
||||
|
||||
formulas: FormulaMap = {
|
||||
account: async () => {
|
||||
account: {
|
||||
formula: async () => {
|
||||
if (this.paymentMethod === 'Cash' && this.paymentType === 'Pay') {
|
||||
return 'Cash';
|
||||
}
|
||||
},
|
||||
paymentAccount: async () => {
|
||||
dependsOn: ['paymentMethod', 'paymentType'],
|
||||
},
|
||||
paymentAccount: {
|
||||
formula: async () => {
|
||||
if (this.paymentMethod === 'Cash' && this.paymentType === 'Receive') {
|
||||
return 'Cash';
|
||||
}
|
||||
},
|
||||
amount: async () => this.getSum('for', 'amount', false),
|
||||
dependsOn: ['paymentMethod', 'paymentType'],
|
||||
},
|
||||
amount: {
|
||||
formula: async () => this.getSum('for', 'amount', false),
|
||||
dependsOn: ['for'],
|
||||
},
|
||||
};
|
||||
|
||||
validations: ValidationMap = {
|
||||
|
@ -4,7 +4,12 @@ import Money from 'pesa/dist/types/src/money';
|
||||
|
||||
export class PaymentFor extends Doc {
|
||||
formulas: FormulaMap = {
|
||||
amount: async () => {
|
||||
amount: {
|
||||
formula: async () => {
|
||||
if (!this.referenceName) {
|
||||
return this.fyo.pesa(0);
|
||||
}
|
||||
|
||||
const outstandingAmount = this.parentdoc!.getFrom(
|
||||
this.referenceType as string,
|
||||
this.referenceName as string,
|
||||
@ -17,6 +22,8 @@ export class PaymentFor extends Doc {
|
||||
|
||||
return this.fyo.pesa(0);
|
||||
},
|
||||
dependsOn: ['referenceName'],
|
||||
},
|
||||
};
|
||||
|
||||
static filters: FiltersMap = {
|
||||
|
@ -1,11 +1,6 @@
|
||||
import { t } from 'fyo';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import {
|
||||
DependsOnMap,
|
||||
FormulaMap,
|
||||
ListsMap,
|
||||
ValidationMap,
|
||||
} from 'fyo/model/types';
|
||||
import { FormulaMap, ListsMap, ValidationMap } from 'fyo/model/types';
|
||||
import { validateEmail } from 'fyo/model/validationFunction';
|
||||
import { getCountryInfo, getFiscalYear } from 'utils/misc';
|
||||
|
||||
@ -30,15 +25,9 @@ export function getCOAList() {
|
||||
}
|
||||
|
||||
export class SetupWizard extends Doc {
|
||||
dependsOn: DependsOnMap = {
|
||||
fiscalYearStart: ['country'],
|
||||
fiscalYearEnd: ['country'],
|
||||
currency: ['country'],
|
||||
chartOfAccounts: ['country'],
|
||||
};
|
||||
|
||||
formulas: FormulaMap = {
|
||||
fiscalYearStart: async () => {
|
||||
fiscalYearStart: {
|
||||
formula: async () => {
|
||||
if (!this.country) return;
|
||||
|
||||
const countryInfo = getCountryInfo();
|
||||
@ -46,30 +35,41 @@ export class SetupWizard extends Doc {
|
||||
countryInfo[this.country as string]?.fiscal_year_start ?? '';
|
||||
return getFiscalYear(fyStart, true);
|
||||
},
|
||||
fiscalYearEnd: async () => {
|
||||
dependsOn: ['country'],
|
||||
},
|
||||
fiscalYearEnd: {
|
||||
formula: async () => {
|
||||
if (!this.country) {
|
||||
return;
|
||||
}
|
||||
|
||||
const countryInfo = getCountryInfo();
|
||||
const fyEnd = countryInfo[this.country as string]?.fiscal_year_end ?? '';
|
||||
const fyEnd =
|
||||
countryInfo[this.country as string]?.fiscal_year_end ?? '';
|
||||
return getFiscalYear(fyEnd, false);
|
||||
},
|
||||
currency: async () => {
|
||||
dependsOn: ['country'],
|
||||
},
|
||||
currency: {
|
||||
formula: async () => {
|
||||
if (!this.country) {
|
||||
return;
|
||||
}
|
||||
const countryInfo = getCountryInfo();
|
||||
return countryInfo[this.country as string]?.currency;
|
||||
},
|
||||
chartOfAccounts: async () => {
|
||||
dependsOn: ['country'],
|
||||
},
|
||||
chartOfAccounts: {
|
||||
formula: async () => {
|
||||
const country = this.get('country') as string | undefined;
|
||||
if (country === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const countryInfo = getCountryInfo();
|
||||
const code = (countryInfo[country] as undefined | { code: string })?.code;
|
||||
const code = (countryInfo[country] as undefined | { code: string })
|
||||
?.code;
|
||||
if (code === undefined) {
|
||||
return;
|
||||
}
|
||||
@ -82,6 +82,8 @@ export class SetupWizard extends Doc {
|
||||
}
|
||||
return coa.name;
|
||||
},
|
||||
dependsOn: ['country'],
|
||||
},
|
||||
};
|
||||
|
||||
validations: ValidationMap = {
|
||||
|
@ -9,10 +9,13 @@ export class TaxSummary extends Doc {
|
||||
baseAmount?: Money;
|
||||
|
||||
formulas: FormulaMap = {
|
||||
baseAmount: async () => {
|
||||
baseAmount: {
|
||||
formula: async () => {
|
||||
const amount = this.amount as Money;
|
||||
const exchangeRate = (this.parentdoc?.exchangeRate ?? 1) as number;
|
||||
return amount.mul(exchangeRate);
|
||||
},
|
||||
dependsOn: ['amount'],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -5,7 +5,8 @@ import { titleCase } from 'utils';
|
||||
|
||||
export class Address extends BaseAddress {
|
||||
formulas: FormulaMap = {
|
||||
addressDisplay: async () => {
|
||||
addressDisplay: {
|
||||
formula: async () => {
|
||||
return [
|
||||
this.addressLine1,
|
||||
this.addressLine2,
|
||||
@ -17,8 +18,18 @@ export class Address extends BaseAddress {
|
||||
.filter(Boolean)
|
||||
.join(', ');
|
||||
},
|
||||
dependsOn: [
|
||||
'addressLine1',
|
||||
'addressLine2',
|
||||
'city',
|
||||
'state',
|
||||
'country',
|
||||
'postalCode',
|
||||
],
|
||||
},
|
||||
|
||||
pos: async () => {
|
||||
pos: {
|
||||
formula: async () => {
|
||||
const stateList = Object.keys(stateCodeMap).map(titleCase).sort();
|
||||
const state = this.state as string;
|
||||
if (stateList.includes(state)) {
|
||||
@ -26,6 +37,8 @@ export class Address extends BaseAddress {
|
||||
}
|
||||
return '';
|
||||
},
|
||||
dependsOn: ['state'],
|
||||
},
|
||||
};
|
||||
|
||||
static lists: ListsMap = {
|
||||
|
@ -18,4 +18,9 @@
|
||||
// 'gstr-2': GoodsAndServiceTaxGSTR2View,
|
||||
// };
|
||||
|
||||
export default {};
|
||||
interface ReportView {
|
||||
title: string;
|
||||
method: string;
|
||||
}
|
||||
|
||||
export default {} as Record<string, ReportView>;
|
@ -2,6 +2,7 @@
|
||||
"name": "Item",
|
||||
"label": "Item",
|
||||
"isSingle": false,
|
||||
"naming": "manual",
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "name",
|
||||
@ -30,11 +31,11 @@
|
||||
"options": [
|
||||
{
|
||||
"value": "Unit",
|
||||
"name": "Unit"
|
||||
"label": "Unit"
|
||||
},
|
||||
{
|
||||
"value": "Kg",
|
||||
"name": "Kg"
|
||||
"label": "Kg"
|
||||
},
|
||||
{
|
||||
"value": "Gram",
|
||||
|
@ -59,6 +59,6 @@
|
||||
"readOnly": true
|
||||
}
|
||||
],
|
||||
"quickEditFields": ["start", "padZeros", "referenceType"],
|
||||
"quickEditFields": ["referenceType", "start", "padZeros"],
|
||||
"keywordFields": []
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "Party",
|
||||
"label": "Party",
|
||||
"naming": "manual",
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "name",
|
||||
@ -33,6 +34,7 @@
|
||||
"label": "Customer"
|
||||
}
|
||||
],
|
||||
"readOnly": true,
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
@ -85,6 +87,7 @@
|
||||
"address",
|
||||
"defaultAccount",
|
||||
"currency",
|
||||
"role",
|
||||
"taxId"
|
||||
],
|
||||
"keywordFields": ["name"]
|
||||
|
@ -6,7 +6,7 @@
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "referenceType",
|
||||
"label": "Reference Type",
|
||||
"label": "Type",
|
||||
"placeholder": "Type",
|
||||
"fieldtype": "Select",
|
||||
"options": [
|
||||
@ -23,7 +23,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "referenceName",
|
||||
"label": "Reference Name",
|
||||
"label": "Name",
|
||||
"fieldtype": "DynamicLink",
|
||||
"references": "referenceType",
|
||||
"placeholder": "Name",
|
||||
@ -31,9 +31,10 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "amount",
|
||||
"label": "Allocated Amount",
|
||||
"label": "Amount",
|
||||
"fieldtype": "Currency",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
],
|
||||
"tableFields": ["referenceType", "referenceName", "amount"]
|
||||
}
|
||||
|
@ -34,6 +34,7 @@
|
||||
"address",
|
||||
"defaultAccount",
|
||||
"currency",
|
||||
"role",
|
||||
"gstType",
|
||||
"gstin"
|
||||
],
|
||||
|
@ -68,7 +68,7 @@ export type Field =
|
||||
| DynamicLinkField
|
||||
| NumberField;
|
||||
|
||||
export type Naming = 'autoincrement' | 'random' | 'numberSeries'
|
||||
export type Naming = 'autoincrement' | 'random' | 'numberSeries' | 'manual';
|
||||
|
||||
export interface Schema {
|
||||
name: string; // Table name
|
||||
|
@ -13,6 +13,18 @@ export default {
|
||||
if (this.value) {
|
||||
this.linkValue = this.value;
|
||||
}
|
||||
|
||||
if (this.df.fieldname === 'incomeAccount') {
|
||||
window.l = this;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
immediate: true,
|
||||
handler(newValue) {
|
||||
this.linkValue = newValue;
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async getSuggestions(keyword = '') {
|
||||
|
@ -14,7 +14,13 @@
|
||||
:class="inputClasses"
|
||||
>
|
||||
<select
|
||||
class="appearance-none bg-transparent focus:outline-none w-11/12 cursor-pointer"
|
||||
class="
|
||||
appearance-none
|
||||
bg-transparent
|
||||
focus:outline-none
|
||||
w-11/12
|
||||
cursor-pointer
|
||||
"
|
||||
:class="{
|
||||
'pointer-events-none': isReadOnly,
|
||||
'text-gray-400': !value,
|
||||
@ -36,6 +42,7 @@
|
||||
</option>
|
||||
</select>
|
||||
<svg
|
||||
v-if="!isReadOnly"
|
||||
class="w-3 h-3"
|
||||
style="background: inherit; margin-right: -3px"
|
||||
viewBox="0 0 5 10"
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="tableFields?.length">
|
||||
<div class="text-gray-600 text-sm mb-1" v-if="showLabel">
|
||||
{{ df.label }}
|
||||
</div>
|
||||
@ -90,6 +90,9 @@ export default {
|
||||
TableRow,
|
||||
},
|
||||
inject: ['doc'],
|
||||
mounted(){
|
||||
window.tab = this
|
||||
},
|
||||
data: () => ({ rowContainerHeight: null }),
|
||||
watch: {
|
||||
value: {
|
||||
|
@ -1,3 +1,6 @@
|
||||
<script setup>
|
||||
const keys = useKeys();
|
||||
</script>
|
||||
<template>
|
||||
<div v-on-outside-click="clearInput" class="relative">
|
||||
<Dropdown :items="suggestions" class="text-sm h-full">
|
||||
@ -13,21 +16,30 @@
|
||||
class="rounded-md relative flex items-center overflow-hidden h-full"
|
||||
>
|
||||
<div class="absolute flex justify-center w-8">
|
||||
<feather-icon name="search" class="w-3 h-3 text-gray-700" />
|
||||
<feather-icon name="search" class="w-3 h-3 text-gray-800" />
|
||||
</div>
|
||||
<input
|
||||
type="search"
|
||||
class="bg-gray-200 text-sm pl-8 focus:outline-none h-full w-56 placeholder-gray-700"
|
||||
class="
|
||||
bg-gray-100
|
||||
text-sm
|
||||
pl-7
|
||||
focus:outline-none
|
||||
h-full
|
||||
w-56
|
||||
placeholder-gray-800
|
||||
"
|
||||
:placeholder="t`Search...`"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
v-model="inputValue"
|
||||
@input="
|
||||
@focus="
|
||||
() => {
|
||||
search();
|
||||
toggleDropdown(true);
|
||||
}
|
||||
"
|
||||
@input="search"
|
||||
ref="input"
|
||||
@keydown.up="highlightItemUp"
|
||||
@keydown.down="highlightItemDown"
|
||||
@ -35,6 +47,12 @@
|
||||
@keydown.tab="toggleDropdown(false)"
|
||||
@keydown.esc="toggleDropdown(false)"
|
||||
/>
|
||||
<div
|
||||
v-if="!inputValue"
|
||||
class="absolute justify-center right-1.5 text-gray-500 px-1.5"
|
||||
>
|
||||
{{ platform === 'Mac' ? '⌘ K' : 'Ctrl K' }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Dropdown>
|
||||
@ -42,10 +60,11 @@
|
||||
</template>
|
||||
<script>
|
||||
import { t } from 'fyo';
|
||||
import reports from 'reports/view';
|
||||
import Dropdown from 'src/components/Dropdown';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { getSearchList } from 'src/utils/search';
|
||||
import { routeTo } from 'src/utils/ui';
|
||||
import { useKeys } from 'src/utils/vueUtils';
|
||||
import { watch } from 'vue';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
@ -61,6 +80,19 @@ export default {
|
||||
emits: ['change'],
|
||||
mounted() {
|
||||
this.makeSearchList();
|
||||
watch(this.keys, (keys) => {
|
||||
if (
|
||||
keys.size === 2 &&
|
||||
keys.has('KeyK') &&
|
||||
(keys.has('MetaLeft') || keys.has('ControlLeft'))
|
||||
) {
|
||||
this.$refs.input.focus();
|
||||
}
|
||||
|
||||
if (keys.size === 1 && keys.has('Escape')) {
|
||||
this.$refs.input.blur();
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
async search() {
|
||||
@ -78,56 +110,17 @@ export default {
|
||||
this.$emit('change', null);
|
||||
},
|
||||
async makeSearchList() {
|
||||
const schemas = this.getSearchableSchemas();
|
||||
const reports = this.getReports();
|
||||
const views = this.getViews();
|
||||
|
||||
let searchList = [...schemas, ...reports, ...views];
|
||||
const searchList = getSearchList();
|
||||
this.searchList = searchList.map((d) => {
|
||||
if (d.route) {
|
||||
d.action = () => routeTo(d.route);
|
||||
if (d.route && !d.action) {
|
||||
d.action = () => {
|
||||
routeTo(d.route);
|
||||
this.inputValue = '';
|
||||
};
|
||||
}
|
||||
return d;
|
||||
});
|
||||
},
|
||||
getSearchableSchemas() {
|
||||
return Object.values(fyo.schemaMap)
|
||||
.filter((s) => !s.isChild && !s.isSingle)
|
||||
.map((s) => ({
|
||||
label: s.label,
|
||||
route: `/list/${s.name}`,
|
||||
group: 'List',
|
||||
}));
|
||||
},
|
||||
getReports() {
|
||||
return Object.values(reports).map((report) => {
|
||||
return {
|
||||
label: report.title,
|
||||
route: `/report/${report.method}`,
|
||||
group: 'Reports',
|
||||
};
|
||||
});
|
||||
},
|
||||
getViews() {
|
||||
return [
|
||||
{
|
||||
label: t`Chart of Accounts`,
|
||||
route: '/chartOfAccounts',
|
||||
group: 'Setup',
|
||||
},
|
||||
{
|
||||
label: t`Data Import`,
|
||||
route: '/data_import',
|
||||
group: 'Setup',
|
||||
},
|
||||
{
|
||||
label: t`Settings`,
|
||||
route: '/settings',
|
||||
group: 'Setup',
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -139,9 +139,6 @@ export default {
|
||||
insertingAccount: false,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.fetchAccounts();
|
||||
},
|
||||
async activated() {
|
||||
this.fetchAccounts();
|
||||
if (fyo.store.isDevelopment) {
|
||||
|
@ -15,7 +15,7 @@
|
||||
flex flex-col
|
||||
justify-between
|
||||
h-full
|
||||
p-6
|
||||
p-4
|
||||
border
|
||||
rounded-lg
|
||||
cursor-pointer
|
||||
|
@ -13,7 +13,9 @@
|
||||
:key="column.label"
|
||||
class="py-4 truncate"
|
||||
:class="
|
||||
['Float', 'Currency'].includes(column.fieldtype) ? 'text-right' : ''
|
||||
['Int', 'Float', 'Currency'].includes(column.fieldtype)
|
||||
? 'text-right'
|
||||
: ''
|
||||
"
|
||||
>
|
||||
{{ column.label }}
|
||||
@ -88,9 +90,16 @@ export default {
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
mounted() {},
|
||||
computed: {
|
||||
columns() {
|
||||
const columns = this.listConfig?.columns ?? [];
|
||||
let columns = this.listConfig?.columns ?? [];
|
||||
|
||||
if (columns.length === 0) {
|
||||
columns = fyo.schemaMap[this.schemaName].quickEditFields ?? [];
|
||||
columns = [...new Set(['name', ...columns])];
|
||||
}
|
||||
|
||||
return columns
|
||||
.map((fieldname) => fyo.getField(this.schemaName, fieldname))
|
||||
.filter(Boolean);
|
||||
@ -123,6 +132,7 @@ export default {
|
||||
routeTo(this.listConfig.formRoute(doc.name));
|
||||
return;
|
||||
}
|
||||
|
||||
openQuickEdit({
|
||||
schemaName: this.schemaName,
|
||||
name: doc.name,
|
||||
|
@ -12,17 +12,15 @@
|
||||
</Button>
|
||||
</template>
|
||||
</PageHeader>
|
||||
<div class="flex-1 flex h-full">
|
||||
<List
|
||||
ref="list"
|
||||
:schemaName="schemaName"
|
||||
:listConfig="listConfig"
|
||||
:filters="filters"
|
||||
class="flex-1"
|
||||
class="flex-1 flex h-full"
|
||||
@makeNewDoc="makeNewDoc"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Button from 'src/components/Button';
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="border-l h-full">
|
||||
<div class="border-l h-full overflow-auto">
|
||||
<!-- Quick edit Tool bar -->
|
||||
<div class="flex items-center justify-between px-4 pt-4">
|
||||
<!-- Close Button and Status Text -->
|
||||
@ -27,7 +27,7 @@
|
||||
</Button>
|
||||
<Button
|
||||
:icon="true"
|
||||
@click="submitDoc"
|
||||
@click="submit"
|
||||
type="primary"
|
||||
v-if="
|
||||
schema?.isSubmittable &&
|
||||
@ -43,19 +43,14 @@
|
||||
</div>
|
||||
|
||||
<!-- Name and image -->
|
||||
<div class="px-4 pt-2 pb-4 flex-center" v-if="doc">
|
||||
<div class="flex flex-col items-center">
|
||||
<div class="p-4 gap-2 flex-center flex flex-col items-center" v-if="doc">
|
||||
<FormControl
|
||||
v-if="imageField"
|
||||
:df="imageField"
|
||||
:value="doc[imageField.fieldname]"
|
||||
@change="(value) => valueChange(imageField, value)"
|
||||
size="small"
|
||||
class="mb-1"
|
||||
:letter-placeholder="
|
||||
// for AttachImage field
|
||||
doc[titleField.fieldname] ? doc[titleField.fieldname][0] : null
|
||||
"
|
||||
:letter-placeholder="doc[titleField.fieldname]?.[0] ?? null"
|
||||
/>
|
||||
<FormControl
|
||||
input-class="text-center"
|
||||
@ -67,7 +62,6 @@
|
||||
@input="setTitleSize"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rest of the form -->
|
||||
<TwoColumnForm
|
||||
@ -229,8 +223,23 @@ export default {
|
||||
}, 300);
|
||||
},
|
||||
async fetchDoc() {
|
||||
if (!this.schemaName) {
|
||||
this.$router.back();
|
||||
}
|
||||
|
||||
if (this.name) {
|
||||
try {
|
||||
this.doc = await fyo.doc.getDoc(this.schemaName, this.name);
|
||||
} catch (e) {
|
||||
this.$router.back();
|
||||
}
|
||||
} else {
|
||||
this.doc = fyo.doc.getNewDoc(this.schemaName);
|
||||
}
|
||||
|
||||
if (this.doc === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.doc.once('afterRename', () => {
|
||||
openQuickEdit({
|
||||
@ -238,32 +247,32 @@ export default {
|
||||
name: this.doc.name,
|
||||
});
|
||||
});
|
||||
|
||||
this.doc.on('beforeSync', () => {
|
||||
this.statusText = t`Saving...`;
|
||||
});
|
||||
|
||||
this.doc.on('afterSync', () => {
|
||||
setTimeout(() => {
|
||||
this.statusText = null;
|
||||
}, 500);
|
||||
});
|
||||
} catch (e) {
|
||||
this.$router.back();
|
||||
}
|
||||
},
|
||||
valueChange(df, value) {
|
||||
this.$refs.form.onChange(df, value);
|
||||
},
|
||||
sync() {
|
||||
this.$refs.form.sync();
|
||||
async sync() {
|
||||
this.statusText = t`Saving`;
|
||||
try {
|
||||
await this.$refs.form.sync();
|
||||
setTimeout(() => {
|
||||
this.statusText = null;
|
||||
}, 300);
|
||||
} catch (err) {
|
||||
this.statusText = null;
|
||||
console.error(err);
|
||||
}
|
||||
},
|
||||
async submitDoc() {
|
||||
async submit() {
|
||||
this.statusText = t`Submitting`;
|
||||
try {
|
||||
await this.$refs.form.submit();
|
||||
} catch (e) {
|
||||
setTimeout(() => {
|
||||
this.statusText = null;
|
||||
console.error(e);
|
||||
}, 300);
|
||||
} catch (err) {
|
||||
this.statusText = null;
|
||||
console.error(err);
|
||||
}
|
||||
},
|
||||
routeToPrevious() {
|
||||
|
123
src/utils/search.ts
Normal file
123
src/utils/search.ts
Normal file
@ -0,0 +1,123 @@
|
||||
import { t } from 'fyo';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import reports from 'reports/view';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { routeTo } from './ui';
|
||||
|
||||
enum SearchGroupEnum {
|
||||
'List' = 'List',
|
||||
'Report' = 'Report',
|
||||
'Create' = 'Create',
|
||||
'Setup' = 'Setup',
|
||||
}
|
||||
|
||||
type SearchGroup = keyof typeof SearchGroupEnum;
|
||||
interface SearchItem {
|
||||
label: string;
|
||||
group: SearchGroup;
|
||||
route?: string;
|
||||
action?: () => void;
|
||||
}
|
||||
|
||||
async function openNewDoc(schemaName: string) {
|
||||
await routeTo(`/list/${schemaName}`);
|
||||
const doc = await fyo.doc.getNewDoc(schemaName);
|
||||
const { openQuickEdit } = await import('src/utils/ui');
|
||||
|
||||
await openQuickEdit({
|
||||
schemaName,
|
||||
name: doc.name as string,
|
||||
});
|
||||
}
|
||||
|
||||
function getCreateList(): SearchItem[] {
|
||||
return [
|
||||
{
|
||||
label: t`Create Item`,
|
||||
group: 'Create',
|
||||
action() {
|
||||
openNewDoc(ModelNameEnum.Item);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t`Create Party`,
|
||||
group: 'Create',
|
||||
action() {
|
||||
openNewDoc(ModelNameEnum.Party);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t`Create Payment`,
|
||||
group: 'Create',
|
||||
action() {
|
||||
openNewDoc(ModelNameEnum.Payment);
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function getReportList(): SearchItem[] {
|
||||
return Object.values(reports).map((report) => {
|
||||
return {
|
||||
label: report.title,
|
||||
route: `/report/${report.method}`,
|
||||
group: 'Report',
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function getListViewList(): SearchItem[] {
|
||||
return [
|
||||
ModelNameEnum.Account,
|
||||
ModelNameEnum.Party,
|
||||
ModelNameEnum.Item,
|
||||
ModelNameEnum.Payment,
|
||||
ModelNameEnum.JournalEntry,
|
||||
ModelNameEnum.PurchaseInvoice,
|
||||
ModelNameEnum.SalesInvoice,
|
||||
ModelNameEnum.Tax,
|
||||
]
|
||||
.map((s) => fyo.schemaMap[s])
|
||||
.filter((s) => s && !s.isChild && !s.isSingle)
|
||||
.map((s) => ({
|
||||
label: s!.label,
|
||||
route: `/list/${s!.name}`,
|
||||
group: 'List',
|
||||
}));
|
||||
}
|
||||
|
||||
function getSetupList(): SearchItem[] {
|
||||
return [
|
||||
{
|
||||
label: t`Chart of Accounts`,
|
||||
route: '/chartOfAccounts',
|
||||
group: 'Setup',
|
||||
},
|
||||
{
|
||||
label: t`Data Import`,
|
||||
route: '/data_import',
|
||||
group: 'Setup',
|
||||
},
|
||||
{
|
||||
label: t`Settings`,
|
||||
route: '/settings',
|
||||
group: 'Setup',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function getSearchList() {
|
||||
const group: Record<SearchGroup, string> = {
|
||||
Create: t`Create`,
|
||||
List: t`List`,
|
||||
Report: t`Report`,
|
||||
Setup: t`Setup`,
|
||||
};
|
||||
|
||||
return [getListViewList(), getCreateList(), getReportList(), getSetupList()]
|
||||
.flat()
|
||||
.map((si) => ({
|
||||
...si,
|
||||
group: group[si.group],
|
||||
}));
|
||||
}
|
@ -125,7 +125,7 @@ export function openSettings(tab: SettingsTab) {
|
||||
routeTo({ path: '/settings', query: { tab } });
|
||||
}
|
||||
|
||||
export function routeTo(route: string | RouteLocationRaw) {
|
||||
export async function routeTo(route: string | RouteLocationRaw) {
|
||||
let routeOptions = route;
|
||||
if (
|
||||
typeof route === 'string' &&
|
||||
@ -138,7 +138,7 @@ export function routeTo(route: string | RouteLocationRaw) {
|
||||
routeOptions = { path: route };
|
||||
}
|
||||
|
||||
router.push(routeOptions);
|
||||
await router.push(routeOptions);
|
||||
}
|
||||
|
||||
export function deleteDocWithPrompt(doc: Doc) {
|
||||
@ -292,6 +292,7 @@ function getDeleteAction(doc: Doc): Action {
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function getDuplicateAction(doc: Doc): Action {
|
||||
const isSubmittable = !!doc.schema.isSubmittable;
|
||||
return {
|
||||
|
31
src/utils/vueUtils.ts
Normal file
31
src/utils/vueUtils.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { onMounted, onUnmounted, Ref, ref } from 'vue';
|
||||
|
||||
export function useKeys(callback?: (keys: Set<string>) => void) {
|
||||
const keys: Ref<Set<string>> = ref(new Set());
|
||||
|
||||
const keydownListener = (e: KeyboardEvent) => {
|
||||
keys.value.add(e.code);
|
||||
callback?.(keys.value);
|
||||
};
|
||||
|
||||
const keyupListener = (e: KeyboardEvent) => {
|
||||
keys.value.delete(e.code);
|
||||
|
||||
// Key up won't trigger on macOS for other keys.
|
||||
if (e.code === 'MetaLeft') {
|
||||
keys.value.clear();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('keydown', keydownListener);
|
||||
window.addEventListener('keyup', keyupListener);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('keydown', keydownListener);
|
||||
window.removeEventListener('keyup', keyupListener);
|
||||
});
|
||||
|
||||
return keys;
|
||||
}
|
@ -415,7 +415,7 @@
|
||||
`Save Template`,`حفظ النموذج`
|
||||
`Save as PDF`,`حفظ كملف PDF`
|
||||
`Save as PDF Successful`,`PDF حفظ كملف ناجح`
|
||||
`Saving...`,`حفظ ...`
|
||||
`Saving`,`حفظ`
|
||||
`Schedule`,`الجدول`
|
||||
`Secured Loans`,`قروض مضمونة`
|
||||
`Securities and Deposits`,`الأوراق المالية والودائع`
|
||||
|
Can't render this file because it has a wrong number of fields in line 62.
|
@ -452,7 +452,7 @@
|
||||
`Save Template`,`Guardar plantilla`
|
||||
`Save as PDF`,`Guardar com a PDF`
|
||||
`Save as PDF Successful`,`Guardat com a PDF correctament`
|
||||
`Saving...`,`Guardant...`
|
||||
`Saving`,`Guardant`
|
||||
`Schedule`,`Programar`
|
||||
`Search...`,`Search...`
|
||||
`Secured Loans`,`Préstecs `
|
||||
|
Can't render this file because it has a wrong number of fields in line 432.
|
@ -374,7 +374,7 @@
|
||||
`Save & Close`,`Speichern & Schließen`
|
||||
`Save as PDF`,`Als PDF speichern`
|
||||
`Save as PDF Successful`,`PDF gespeichert`
|
||||
`Saving...`,`Speichern...`
|
||||
`Saving`,`Speichern`
|
||||
`Schedule`,`Zeitplan`
|
||||
`Secured Loans`,`Gesicherte Darlehen`
|
||||
`Securities and Deposits`,`Wertpapiere und Einlagen`
|
||||
|
Can't render this file because it has a wrong number of fields in line 33.
|
@ -374,7 +374,7 @@
|
||||
`Save & Close`,`Enregistrer et fermer`
|
||||
`Save as PDF`,`Enregistrer en PDF`
|
||||
`Save as PDF Successful`,`Enregistrer en PDF avec succès`
|
||||
`Saving...`,`Sauver...`
|
||||
`Saving`,`Sauver`
|
||||
`Schedule`,`Programme`
|
||||
`Secured Loans`,`Prêts garantis`
|
||||
`Securities and Deposits`,`Titres et dépôts`
|
||||
|
Can't render this file because it has a wrong number of fields in line 57.
|
@ -374,7 +374,7 @@
|
||||
`Save & Close`,`Guardar e Fechar`
|
||||
`Save as PDF`,`Guardar como PDF`
|
||||
`Save as PDF Successful`,`Guardar como PDF Bem sucedido`
|
||||
`Saving...`,`Salvar...`
|
||||
`Saving`,`Salvar`
|
||||
`Schedule`,`Horário`
|
||||
`Secured Loans`,`Empréstimos Garantidos`
|
||||
`Securities and Deposits`,`Títulos e Depósitos`
|
||||
|
Can't render this file because it has a wrong number of fields in line 57.
|
Loading…
Reference in New Issue
Block a user