diff --git a/models/baseModels/ERPNextSyncSettings/ERPNextSyncSettings.ts b/models/baseModels/ERPNextSyncSettings/ERPNextSyncSettings.ts index b163e153..329b64f7 100644 --- a/models/baseModels/ERPNextSyncSettings/ERPNextSyncSettings.ts +++ b/models/baseModels/ERPNextSyncSettings/ERPNextSyncSettings.ts @@ -1,6 +1,7 @@ import { Doc } from 'fyo/model/doc'; import { ChangeArg, HiddenMap } from 'fyo/model/types'; import { initERPNSync, syncDocumentsToERPNext } from 'src/utils/erpnextSync'; +import { ErrorLogEnum } from 'fyo/telemetry/types'; export class ERPNextSyncSettings extends Doc { deviceID?: string; @@ -44,17 +45,57 @@ export class ERPNextSyncSettings extends Doc { async change(ch: ChangeArg) { if (ch.changed === 'syncDataFromServer') { - const { showToast } = await import('src/utils/interactive'); - showToast({ - type: 'warning', - message: 'Fetching data from server.', - duration: 'very_long', - }); - await initERPNSync(this.fyo); - ipc.reloadWindow(); + try { + const { showToast } = await import('src/utils/interactive'); + showToast({ + type: 'warning', + message: 'Fetching data from server.', + duration: 'very_long', + }); + await initERPNSync(this.fyo); + ipc.reloadWindow(); + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + + try { + await this.fyo.doc + .getNewDoc(ErrorLogEnum.IntegrationErrorLog, { + error: errorMessage, + data: JSON.stringify({ + instance: this.deviceID, + operation: 'sync_data_from_server', + trigger: 'change_event', + }), + }) + .sync(); + } catch (logError) { + throw logError; + } + } } else if (ch.changed === 'syncDataToServer') { - await syncDocumentsToERPNext(this.fyo); - ipc.reloadWindow(); + try { + await syncDocumentsToERPNext(this.fyo); + ipc.reloadWindow(); + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + + try { + await this.fyo.doc + .getNewDoc(ErrorLogEnum.IntegrationErrorLog, { + error: errorMessage, + data: JSON.stringify({ + instance: this.deviceID, + operation: 'sync_data_to_server', + trigger: 'change_event', + }), + }) + .sync(); + } catch (logError) { + throw logError; + } + } } } } diff --git a/schemas/app/IntegrationErrorLog.json b/schemas/app/IntegrationErrorLog.json index 5710f3cd..06b030ac 100644 --- a/schemas/app/IntegrationErrorLog.json +++ b/schemas/app/IntegrationErrorLog.json @@ -10,19 +10,26 @@ "fieldtype": "Data", "required": true }, + { + "fieldname": "spacer", + "fieldtype": "Text", + "invisible": true + }, { "fieldname": "data", "label": "Data", "readonly": true, "fieldtype": "Text", - "section": "Default" + "section": "Default", + "rows": 20 }, { "fieldname": "error", "label": "Error", "readonly": true, "fieldtype": "Text", - "section": "Default" + "section": "Default", + "rows": 20 } ], "keywordFields": ["name"] diff --git a/schemas/types.ts b/schemas/types.ts index 080dce3d..63d7ddf8 100644 --- a/schemas/types.ts +++ b/schemas/types.ts @@ -57,6 +57,7 @@ export interface BaseField { 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 + invisible?: boolean; // UI Facing config, whether field is invisible but occupies space readOnly?: boolean; // UI Facing config, whether field is editable description?: string; // UI Facing, translateable, used for inline documentation default?: RawValue; // Default value of a field, should match the db type @@ -73,6 +74,7 @@ export interface BaseField { sub_label?: string; filters?: Record; getOptions?: () => Promise<{ label: string; value: string }[]>; + rows?: number; // UI Facing config, number of rows for Text field (default 3) } export type SelectOption = { value: string; label: string }; diff --git a/src/components/Controls/Text.vue b/src/components/Controls/Text.vue index 3df80a20..a5ba66a6 100644 --- a/src/components/Controls/Text.vue +++ b/src/components/Controls/Text.vue @@ -6,7 +6,7 @@