diff --git a/.eslintrc.js b/.eslintrc.js index 7574bf64..2b09c43c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -10,6 +10,7 @@ module.exports = { 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 'arrow-body-style': 'off', 'prefer-arrow-callback': 'off', + 'vue/no-mutating-props': 'off', 'vue/multi-word-component-names': 'off', 'vue/no-useless-template-attributes': 'off', }, diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c78e4097..d49750f2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,11 +24,8 @@ jobs: - name: Install Dependencies run: yarn - - name: Install RPM - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install rpm - - name: Run build env: CSC_IDENTITY_AUTO_DISCOVERY: false APPLE_NOTARIZE: 0 - run: yarn electron:build -mwl --publish never + run: yarn electron:build -mw --publish never diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 29a6c700..b1676bdf 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -34,13 +34,12 @@ jobs: - name: Run build env: APPLE_ID: ${{ secrets.APPLE_ID }} - APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} CSC_LINK: ${{ secrets.CSC_LINK }} CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} CSC_IDENTITY_AUTO_DISCOVERY: true GH_TOKEN: ${{ secrets.GH_TOKEN }} - APPLE_NOTARIZE: 1 run: | yarn set version 1.22.18 yarn electron:build --mac --publish always diff --git a/README.md b/README.md index 7ed78e16..70335ca6 100644 --- a/README.md +++ b/README.md @@ -121,8 +121,7 @@ If you want to contribute code then you can fork this repo, make changes and rai ## Links -- [Telegram Group](https://t.me/frappebooks): Used for discussions regarding features, issues, changes, etc. This group is also be used to make decisions regarding project direction. -- [Project Board](https://github.com/frappe/books/projects/1): Roadmap that is updated with acceptable latency. +- [Telegram Group](https://t.me/frappebooks): Used for discussions and decisions regarding everything Frappe Books. - [GitHub Discussions](https://github.com/frappe/books/discussions): Used for discussions around a specific topic. - [Frappe Books Blog](https://tech.frappebooks.com/): Sporadically updated dev blog regarding the development of this project. diff --git a/backend/patches/createInventoryNumberSeries.ts b/backend/patches/createInventoryNumberSeries.ts index 2852691a..96410427 100644 --- a/backend/patches/createInventoryNumberSeries.ts +++ b/backend/patches/createInventoryNumberSeries.ts @@ -2,9 +2,19 @@ import { getDefaultMetaFieldValueMap } from '../../backend/helpers'; import { DatabaseManager } from '../database/manager'; async function execute(dm: DatabaseManager) { + const s = (await dm.db?.getAll('SingleValue', { + fields: ['value'], + filters: { fieldname: 'setupComplete' }, + })) as { value: string }[]; + + if (!Number(s?.[0]?.value ?? '0')) { + return; + } + const names: Record = { StockMovement: 'SMOV-', - Shipment: 'SHP-', + PurchaseReceipt: 'PREC-', + Shipment: 'SHPM-', }; for (const referenceType in names) { diff --git a/build/notarize.js b/build/notarize.js deleted file mode 100644 index 56fdfeb6..00000000 --- a/build/notarize.js +++ /dev/null @@ -1,21 +0,0 @@ -const { notarize } = require('electron-notarize'); - -exports.default = async (context) => { - const { electronPlatformName, appOutDir } = context; - if ( - electronPlatformName !== 'darwin' || - !parseInt(process.env.APPLE_NOTARIZE) - ) { - return; - } - - const appName = context.packager.appInfo.productFilename; - - return await notarize({ - appBundleId: 'io.frappe.books', - appPath: `${appOutDir}/${appName}.app`, - appleId: process.env.APPLE_ID, - appleIdPassword: process.env.APPLE_APP_PASSWORD, - teamId: process.env.APPLE_TEAM_ID, - }); -}; diff --git a/electron-builder.yml b/electron-builder.yml index 63de2d59..95647edb 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -1,6 +1,5 @@ productName: Frappe Books appId: io.frappe.books -afterSign: build/notarize.js asarUnpack: '**/*.node' extraResources: [ @@ -11,6 +10,8 @@ mac: type: distribution category: public.app-category.finance icon: build/icon.icns + notarize: + appBundleId: io.frappe.books hardenedRuntime: true gatekeeperAssess: false darkModeSupport: false diff --git a/fyo/core/converter.ts b/fyo/core/converter.ts index 7a3c2d55..c9220163 100644 --- a/fyo/core/converter.ts +++ b/fyo/core/converter.ts @@ -3,7 +3,6 @@ import { Doc } from 'fyo/model/doc'; import { isPesa } from 'fyo/utils'; import { ValueError } from 'fyo/utils/errors'; import { DateTime } from 'luxon'; -import { Money } from 'pesa'; import { Field, FieldTypeEnum, RawValue, TargetField } from 'schemas/types'; import { getIsNullOrUndef, safeParseFloat, safeParseInt } from 'utils'; import { DatabaseHandler } from './dbHandler'; diff --git a/fyo/model/doc.ts b/fyo/model/doc.ts index c78d9aba..18128bfd 100644 --- a/fyo/model/doc.ts +++ b/fyo/model/doc.ts @@ -5,7 +5,6 @@ import { Verb } from 'fyo/telemetry/types'; import { DEFAULT_USER } from 'fyo/utils/consts'; import { ConflictError, MandatoryError, NotFoundError } from 'fyo/utils/errors'; import Observable from 'fyo/utils/observable'; -import { Money } from 'pesa'; import { DynamicLinkField, Field, @@ -142,6 +141,10 @@ export class Doc extends Observable { return false; } + if (this.schema.isChild) { + return false; + } + if (!this.schema.isSubmittable) { return true; } @@ -186,6 +189,14 @@ export class Doc extends Observable { return false; } + if (this.schema.isSingle) { + return false; + } + + if (this.schema.isChild) { + return false; + } + return true; } diff --git a/fyo/utils/format.ts b/fyo/utils/format.ts index d5c80df1..335addb9 100644 --- a/fyo/utils/format.ts +++ b/fyo/utils/format.ts @@ -188,7 +188,7 @@ function getField(df: string | Field): Field { label: '', fieldname: '', fieldtype: df as FieldType, - }; + } as Field; } return df; diff --git a/fyo/utils/index.ts b/fyo/utils/index.ts index c35bbff9..e7bad878 100644 --- a/fyo/utils/index.ts +++ b/fyo/utils/index.ts @@ -1,8 +1,9 @@ import { Fyo } from 'fyo'; +import { DocValue } from 'fyo/core/types'; import { Doc } from 'fyo/model/doc'; import { Action } from 'fyo/model/types'; import { Money } from 'pesa'; -import { Field, OptionField, SelectOption } from 'schemas/types'; +import { Field, FieldType, OptionField, SelectOption } from 'schemas/types'; import { getIsNullOrUndef, safeParseInt } from 'utils'; export function slug(str: string) { @@ -109,3 +110,34 @@ function getRawOptionList(field: Field, doc: Doc | undefined | null) { return getList(doc!); } + +export function getEmptyValuesByFieldTypes( + fieldtype: FieldType, + fyo: Fyo +): DocValue { + switch (fieldtype) { + case 'Date': + case 'Datetime': + return new Date(); + case 'Float': + case 'Int': + return 0; + case 'Currency': + return fyo.pesa(0); + case 'Check': + return false; + case 'DynamicLink': + case 'Link': + case 'Select': + case 'AutoComplete': + case 'Text': + case 'Data': + case 'Color': + return null; + case 'Table': + case 'Attachment': + case 'AttachImage': + default: + return null; + } +} diff --git a/models/inventory/StockMovement.ts b/models/inventory/StockMovement.ts index 070d8c95..15632c19 100644 --- a/models/inventory/StockMovement.ts +++ b/models/inventory/StockMovement.ts @@ -6,7 +6,12 @@ import { FormulaMap, ListViewSettings, } from 'fyo/model/types'; -import { addItem, getDocStatusListColumn, getLedgerLinkAction } from 'models/helpers'; +import { ValidationError } from 'fyo/utils/errors'; +import { + addItem, + getDocStatusListColumn, + getLedgerLinkAction, +} from 'models/helpers'; import { LedgerPosting } from 'models/Transactional/LedgerPosting'; import { ModelNameEnum } from 'models/types'; import { Money } from 'pesa'; @@ -42,6 +47,24 @@ export class StockMovement extends Transfer { }, }; + async validate() { + await super.validate(); + if (this.movementType !== MovementType.Manufacture) { + return; + } + + const hasFrom = this.items?.findIndex((f) => f.fromLocation) !== -1; + const hasTo = this.items?.findIndex((f) => f.toLocation) !== -1; + + if (!hasFrom) { + throw new ValidationError(this.fyo.t`Item with From location not found`); + } + + if (!hasTo) { + throw new ValidationError(this.fyo.t`Item with To location not found`); + } + } + static filters: FiltersMap = { numberSeries: () => ({ referenceType: ModelNameEnum.StockMovement }), }; @@ -68,6 +91,7 @@ export class StockMovement extends Transfer { [MovementType.MaterialIssue]: fyo.t`Material Issue`, [MovementType.MaterialReceipt]: fyo.t`Material Receipt`, [MovementType.MaterialTransfer]: fyo.t`Material Transfer`, + [MovementType.Manufacture]: fyo.t`Manufacture`, }[movementType] ?? ''; return { diff --git a/models/inventory/StockMovementItem.ts b/models/inventory/StockMovementItem.ts index 7ce8742e..1276227a 100644 --- a/models/inventory/StockMovementItem.ts +++ b/models/inventory/StockMovementItem.ts @@ -4,7 +4,9 @@ import { FormulaMap, ReadOnlyMap, RequiredMap, + ValidationMap, } from 'fyo/model/types'; +import { ValidationError } from 'fyo/utils/errors'; import { ModelNameEnum } from 'models/types'; import { Money } from 'pesa'; import { StockMovement } from './StockMovement'; @@ -33,6 +35,10 @@ export class StockMovementItem extends Doc { return this.parentdoc?.movementType === MovementType.MaterialTransfer; } + get isManufacture() { + return this.parentdoc?.movementType === MovementType.Manufacture; + } + static filters: FiltersMap = { item: () => ({ trackItem: true }), }; @@ -53,14 +59,14 @@ export class StockMovementItem extends Doc { dependsOn: ['item', 'rate', 'quantity'], }, fromLocation: { - formula: (fn) => { - if (this.isReceipt || this.isTransfer) { + formula: () => { + if (this.isReceipt || this.isTransfer || this.isManufacture) { return null; } const defaultLocation = this.fyo.singles.InventorySettings ?.defaultLocation as string | undefined; - if (defaultLocation && !this.location && this.isIssue) { + if (defaultLocation && !this.fromLocation && this.isIssue) { return defaultLocation; } @@ -69,14 +75,14 @@ export class StockMovementItem extends Doc { dependsOn: ['movementType'], }, toLocation: { - formula: (fn) => { - if (this.isIssue || this.isTransfer) { + formula: () => { + if (this.isIssue || this.isTransfer || this.isManufacture) { return null; } const defaultLocation = this.fyo.singles.InventorySettings ?.defaultLocation as string | undefined; - if (defaultLocation && !this.location && this.isReceipt) { + if (defaultLocation && !this.toLocation && this.isReceipt) { return defaultLocation; } @@ -86,6 +92,31 @@ export class StockMovementItem extends Doc { }, }; + validations: ValidationMap = { + fromLocation: (value) => { + if (!this.isManufacture) { + return; + } + + if (value && this.toLocation) { + throw new ValidationError( + this.fyo.t`Only From or To can be set for Manucature` + ); + } + }, + toLocation: (value) => { + if (!this.isManufacture) { + return; + } + + if (value && this.fromLocation) { + throw new ValidationError( + this.fyo.t`Only From or To can be set for Manufacture` + ); + } + }, + }; + required: RequiredMap = { fromLocation: () => this.isIssue || this.isTransfer, toLocation: () => this.isReceipt || this.isTransfer, diff --git a/models/inventory/tests/testStockMovement.spec.ts b/models/inventory/tests/testStockMovement.spec.ts new file mode 100644 index 00000000..8de6a767 --- /dev/null +++ b/models/inventory/tests/testStockMovement.spec.ts @@ -0,0 +1,136 @@ +import { ModelNameEnum } from 'models/types'; +import test from 'tape'; +import { getItem } from './helpers'; +import { closeTestFyo, getTestFyo, setupTestFyo } from 'tests/helpers'; +import { MovementType } from '../types'; +import { + assertDoesNotThrow, + assertThrows, +} from 'backend/database/tests/helpers'; +import { StockMovement } from '../StockMovement'; + +const fyo = getTestFyo(); +setupTestFyo(fyo, __filename); + +test('check store and create test items', async (t) => { + const e = await fyo.db.exists(ModelNameEnum.Location, 'Stores'); + t.equals(e, true, 'location Stores exist'); + + const items = [ + getItem('RawOne', 100), + getItem('RawTwo', 100), + getItem('Final', 200), + ]; + + const exists: boolean[] = []; + for (const item of items) { + await fyo.doc.getNewDoc('Item', item).sync(); + exists.push(await fyo.db.exists('Item', item.name)); + } + + t.ok(exists.every(Boolean), 'items created'); +}); + +test('Stock Movement, Material Receipt', async (t) => { + const sm = fyo.doc.getNewDoc(ModelNameEnum.StockMovement); + + await sm.set({ + date: new Date('2022-01-01'), + movementType: MovementType.MaterialReceipt, + }); + + await sm.append('items', { + item: 'RawOne', + quantity: 1, + rate: 100, + toLocation: 'Stores', + }); + + await sm.append('items', { + item: 'RawTwo', + quantity: 1, + rate: 100, + toLocation: 'Stores', + }); + + await assertDoesNotThrow(async () => await sm.sync()); + await assertDoesNotThrow(async () => await sm.submit()); + + t.equal( + await fyo.db.getStockQuantity('RawOne', 'Stores'), + 1, + 'item RawOne added' + ); + t.equal( + await fyo.db.getStockQuantity('RawTwo', 'Stores'), + 1, + 'item RawTwo added' + ); + t.equal( + await fyo.db.getStockQuantity('Final', 'Stores'), + null, + 'item Final not yet added' + ); +}); + +test('Stock Movement, Manufacture', async (t) => { + const sm = fyo.doc.getNewDoc(ModelNameEnum.StockMovement) as StockMovement; + + await sm.set({ + date: new Date('2022-01-02'), + movementType: MovementType.Manufacture, + }); + + await sm.append('items', { + item: 'RawOne', + quantity: 1, + rate: 100, + }); + + await assertDoesNotThrow( + async () => await sm.items?.[0].set('fromLocation', 'Stores') + ); + await assertThrows( + async () => await sm.items?.[0].set('toLocation', 'Stores') + ); + t.notOk(sm.items?.[0].to, 'to location not set'); + + await sm.append('items', { + item: 'RawTwo', + quantity: 1, + rate: 100, + fromLocation: 'Stores', + }); + + await assertThrows(async () => await sm.sync()); + + await sm.append('items', { + item: 'Final', + quantity: 1, + rate: 100, + toLocation: 'Stores', + }); + + await assertDoesNotThrow(async () => await sm.sync()); + await assertDoesNotThrow(async () => await sm.submit()); + + t.equal( + await fyo.db.getStockQuantity('RawOne', 'Stores'), + 0, + 'item RawOne removed' + ); + + t.equal( + await fyo.db.getStockQuantity('RawTwo', 'Stores'), + 0, + 'item RawTwo removed' + ); + + t.equal( + await fyo.db.getStockQuantity('Final', 'Stores'), + 1, + 'item Final added' + ); +}); + +closeTestFyo(fyo, __filename); diff --git a/models/inventory/types.ts b/models/inventory/types.ts index 0809a0e4..c07bc3e4 100644 --- a/models/inventory/types.ts +++ b/models/inventory/types.ts @@ -9,6 +9,7 @@ export enum MovementType { 'MaterialIssue' = 'MaterialIssue', 'MaterialReceipt' = 'MaterialReceipt', 'MaterialTransfer' = 'MaterialTransfer', + 'Manufacture' = 'Manufacture', } export interface SMDetails { diff --git a/package.json b/package.json index 9fc4e14a..06fda008 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "frappe-books", - "version": "0.8.0", + "version": "0.9.0", "description": "Simple book-keeping app for everyone", "main": "background.js", "author": { @@ -55,10 +55,9 @@ "autoprefixer": "^9", "babel-loader": "^8.2.3", "dotenv": "^16.0.0", - "electron": "^18.3.7", - "electron-builder": "^23.0.3", + "electron": "18.3.7", + "electron-builder": "24.0.0-alpha.12", "electron-devtools-installer": "^3.2.0", - "electron-notarize": "^1.1.1", "electron-rebuild": "^3.2.9", "electron-updater": "^5.2.1", "eslint": "^7.32.0", @@ -81,7 +80,7 @@ "webpack": "^5.66.0" }, "resolutions": { - "electron-builder": "^23.3.3" + "electron-builder": "24.0.0-alpha.12" }, "prettier": { "semi": true, diff --git a/reports/types.ts b/reports/types.ts index 416df57b..8e010b44 100644 --- a/reports/types.ts +++ b/reports/types.ts @@ -1,6 +1,6 @@ import { DateTime } from 'luxon'; import { AccountRootType } from 'models/baseModels/Account/types'; -import { BaseField, RawValue } from 'schemas/types'; +import { BaseField, FieldType, RawValue } from 'schemas/types'; export type ExportExtention = 'csv' | 'json'; @@ -24,7 +24,8 @@ export interface ReportRow { foldedBelow?: boolean; } export type ReportData = ReportRow[]; -export interface ColumnField extends BaseField { +export interface ColumnField extends Omit { + fieldtype: FieldType; align?: 'left' | 'right' | 'center'; width?: number; } diff --git a/schemas/app/Party.json b/schemas/app/Party.json index 96ceff80..820efd1d 100644 --- a/schemas/app/Party.json +++ b/schemas/app/Party.json @@ -73,7 +73,6 @@ "label": "Address", "fieldtype": "Link", "target": "Address", - "placeholder": "Click to create", "inline": true }, { diff --git a/schemas/app/PrintSettings.json b/schemas/app/PrintSettings.json index ed88843e..1499093e 100644 --- a/schemas/app/PrintSettings.json +++ b/schemas/app/PrintSettings.json @@ -40,7 +40,6 @@ "label": "Address", "fieldtype": "Link", "target": "Address", - "placeholder": "Click to create", "inline": true }, { diff --git a/schemas/app/inventory/Location.json b/schemas/app/inventory/Location.json index 6926aaee..45161032 100644 --- a/schemas/app/inventory/Location.json +++ b/schemas/app/inventory/Location.json @@ -16,7 +16,6 @@ "label": "Address", "fieldtype": "Link", "target": "Address", - "placeholder": "Click to create", "inline": true } ], diff --git a/schemas/app/inventory/StockMovement.json b/schemas/app/inventory/StockMovement.json index b4d41684..99651250 100644 --- a/schemas/app/inventory/StockMovement.json +++ b/schemas/app/inventory/StockMovement.json @@ -35,6 +35,10 @@ { "value": "MaterialTransfer", "label": "Material Transfer" + }, + { + "value": "Manufacture", + "label": "Manufacture" } ], "required": true diff --git a/schemas/tests/Party.json b/schemas/tests/Party.json index 121a7065..b833ec4e 100644 --- a/schemas/tests/Party.json +++ b/schemas/tests/Party.json @@ -73,7 +73,6 @@ "label": "Address", "fieldtype": "Link", "target": "Address", - "placeholder": "Click to create", "inline": true } ], diff --git a/schemas/types.ts b/schemas/types.ts index 10ffa0b8..2cf49c28 100644 --- a/schemas/types.ts +++ b/schemas/types.ts @@ -1,28 +1,56 @@ -export enum FieldTypeEnum { - Data = 'Data', - Select = 'Select', - Link = 'Link', - Date = 'Date', - Datetime = 'Datetime', - Table = 'Table', - AutoComplete = 'AutoComplete', - Check = 'Check', - AttachImage = 'AttachImage', - DynamicLink = 'DynamicLink', - Int = 'Int', - Float = 'Float', - Currency = 'Currency', - Text = 'Text', - Color = 'Color', - Attachment = 'Attachment', -} +import { PropertyEnum } from "utils/types"; + +export type FieldType = + | 'Data' + | 'Select' + | 'Link' + | 'Date' + | 'Datetime' + | 'Table' + | 'AutoComplete' + | 'Check' + | 'AttachImage' + | 'DynamicLink' + | 'Int' + | 'Float' + | 'Currency' + | 'Text' + | 'Color' + | 'Attachment'; + +export const FieldTypeEnum: PropertyEnum> = { + Data: 'Data', + Select: 'Select', + Link: 'Link', + Date: 'Date', + Datetime: 'Datetime', + Table: 'Table', + AutoComplete: 'AutoComplete', + Check: 'Check', + AttachImage: 'AttachImage', + DynamicLink: 'DynamicLink', + Int: 'Int', + Float: 'Float', + Currency: 'Currency', + Text: 'Text', + Color: 'Color', + Attachment: 'Attachment', +}; + +type OptionFieldType = 'Select' | 'AutoComplete' | 'Color'; +type TargetFieldType = 'Table' | 'Link'; +type NumberFieldType = 'Int' | 'Float'; +type DynamicLinkFieldType = 'DynamicLink'; +type BaseFieldType = Exclude< + FieldType, + TargetFieldType | DynamicLinkFieldType | OptionFieldType | NumberFieldType +>; -export type FieldType = keyof typeof FieldTypeEnum; export type RawValue = string | number | boolean | null; export interface BaseField { - fieldname: string; // Column name in the db - fieldtype: FieldType; // UI Descriptive field types that map to column types + fieldname: string; // Column name in the db + fieldtype: BaseFieldType; // 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 @@ -39,31 +67,28 @@ export interface BaseField { } export type SelectOption = { value: string; label: string }; -export interface OptionField extends BaseField { - fieldtype: - | FieldTypeEnum.Select - | FieldTypeEnum.AutoComplete - | FieldTypeEnum.Color; +export interface OptionField extends Omit { + fieldtype: OptionFieldType; options: SelectOption[]; emptyMessage?: string; allowCustom?: boolean; } -export interface TargetField extends BaseField { - fieldtype: FieldTypeEnum.Table | FieldTypeEnum.Link; +export interface TargetField extends Omit { + fieldtype: TargetFieldType; target: string; // Name of the table or group of tables to fetch values create?: boolean; // Whether to show Create in the dropdown edit?: boolean; // Whether the Table has quick editable columns } -export interface DynamicLinkField extends BaseField { - fieldtype: FieldTypeEnum.DynamicLink; +export interface DynamicLinkField extends Omit { + fieldtype: DynamicLinkFieldType; emptyMessage?: string; references: string; // Reference to an option field that links to schema } -export interface NumberField extends BaseField { - fieldtype: FieldTypeEnum.Float | FieldTypeEnum.Int; +export interface NumberField extends Omit { + fieldtype: NumberFieldType; minvalue?: number; // UI Facing used to restrict lower bound maxvalue?: number; // UI Facing used to restrict upper bound } @@ -80,7 +105,7 @@ export type Naming = 'autoincrement' | 'random' | 'numberSeries' | 'manual'; export interface Schema { name: string; // Table name label: string; // Translateable UI facing name - fields: Field[]; // Maps to database columns + fields: Field[]; // Maps to database columns isTree?: boolean; // Used for nested set, eg for Chart of Accounts extends?: string; // Value points to an Abstract schema. Indicates Subclass schema isChild?: boolean; // Indicates a child table, i.e table with "parent" FK column diff --git a/src/App.vue b/src/App.vue index 40cb66de..f3312327 100644 --- a/src/App.vue +++ b/src/App.vue @@ -41,6 +41,7 @@ import { ConfigKeys } from 'fyo/core/types'; import { RTL_LANGUAGES } from 'fyo/utils/consts'; import { ModelNameEnum } from 'models/types'; +import { systemLanguageRef } from 'src/utils/refs'; import { computed } from 'vue'; import WindowsTitleBar from './components/WindowsTitleBar.vue'; import { handleErrorWithDialog } from './errorHandling'; @@ -54,6 +55,7 @@ import { initializeInstance } from './utils/initialization'; import { checkForUpdates } from './utils/ipcCalls'; import { updateConfigFiles } from './utils/misc'; import { Search } from './utils/search'; +import { setGlobalShortcuts } from './utils/shortcuts'; import { routeTo } from './utils/ui'; import { Shortcuts, useKeys } from './utils/vueUtils'; @@ -69,8 +71,6 @@ export default { companyName: '', searcher: null, shortcuts: null, - languageDirection: 'ltr', - language: '', }; }, provide() { @@ -88,11 +88,8 @@ export default { WindowsTitleBar, }, async mounted() { - this.language = fyo.config.get('language'); - this.languageDirection = RTL_LANGUAGES.includes(this.language) - ? 'rtl' - : 'ltr'; - this.shortcuts = new Shortcuts(this.keys); + const shortcuts = new Shortcuts(this.keys); + this.shortcuts = shortcuts; const lastSelectedFilePath = fyo.config.get( ConfigKeys.LastSelectedFilePath, null @@ -108,6 +105,16 @@ export default { await handleErrorWithDialog(err, undefined, true, true); await this.showDbSelector(); } + + setGlobalShortcuts(shortcuts); + }, + computed: { + language() { + return systemLanguageRef.value; + }, + languageDirection() { + return RTL_LANGUAGES.includes(this.language) ? 'rtl' : 'ltr'; + }, }, methods: { async setDesk(filePath) { diff --git a/src/components/Controls/AutoComplete.vue b/src/components/Controls/AutoComplete.vue index 321289fb..ab65c594 100644 --- a/src/components/Controls/AutoComplete.vue +++ b/src/components/Controls/AutoComplete.vue @@ -108,6 +108,10 @@ export default { option = this.options.find((o) => o.label === value); } + if (!value && option === undefined) { + return null; + } + return option?.label ?? oldValue; }, async updateSuggestions(keyword) { diff --git a/src/components/Controls/Select.vue b/src/components/Controls/Select.vue index cc5e154e..087c04ba 100644 --- a/src/components/Controls/Select.vue +++ b/src/components/Controls/Select.vue @@ -23,7 +23,7 @@ @change="(e) => triggerChange(e.target.value)" @focus="(e) => $emit('focus', e)" > -