diff --git a/crm/fcrm/doctype/crm_deal/crm_deal.py b/crm/fcrm/doctype/crm_deal/crm_deal.py index 6bc2e020..2bf3d8d4 100644 --- a/crm/fcrm/doctype/crm_deal/crm_deal.py +++ b/crm/fcrm/doctype/crm_deal/crm_deal.py @@ -25,7 +25,7 @@ class CRMDeal(Document): add_status_change_log(self) if frappe.db.get_value("CRM Deal Status", self.status, "type") == "Won": self.closed_date = frappe.utils.nowdate() - self.validate_forcasting_fields() + self.validate_forecasting_fields() self.validate_lost_reason() self.update_exchange_rate() @@ -151,9 +151,21 @@ class CRMDeal(Document): if not self.probability or self.probability == 0: self.probability = frappe.db.get_value("CRM Deal Status", self.status, "probability") or 0 - def validate_forcasting_fields(self): + def update_expected_deal_value(self): + """ + Update the expected deal value based on the net total or total. + """ + if ( + frappe.db.get_single_value("FCRM Settings", "auto_update_expected_deal_value") + and (self.net_total or self.total) + and self.expected_deal_value + ): + self.expected_deal_value = self.net_total or self.total + + def validate_forecasting_fields(self): self.update_closed_date() self.update_default_probability() + self.update_expected_deal_value() if frappe.db.get_single_value("FCRM Settings", "enable_forecasting"): if not self.expected_deal_value or self.expected_deal_value == 0: frappe.throw(_("Expected Deal Value is required."), frappe.MandatoryError) diff --git a/crm/fcrm/doctype/fcrm_settings/fcrm_settings.json b/crm/fcrm/doctype/fcrm_settings/fcrm_settings.json index 156144e1..5ff58822 100644 --- a/crm/fcrm/doctype/fcrm_settings/fcrm_settings.json +++ b/crm/fcrm/doctype/fcrm_settings/fcrm_settings.json @@ -8,6 +8,7 @@ "defaults_tab", "restore_defaults", "enable_forecasting", + "auto_update_expected_deal_value", "currency_tab", "currency", "exchange_rate_provider_section", @@ -105,12 +106,19 @@ { "fieldname": "column_break_vqck", "fieldtype": "Column Break" + }, + { + "default": "1", + "description": "Automatically update \"Expected Deal Value\" based on the total value of associated products in a deal", + "fieldname": "auto_update_expected_deal_value", + "fieldtype": "Check", + "label": "Auto Update Expected Deal Value" } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2025-07-29 11:26:50.420614", + "modified": "2025-09-16 17:33:26.406549", "modified_by": "Administrator", "module": "FCRM", "name": "FCRM Settings", diff --git a/frontend/components.d.ts b/frontend/components.d.ts index 43fe5a81..0e89df20 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -44,7 +44,7 @@ declare module 'vue' { Autocomplete: typeof import('./src/components/frappe-ui/Autocomplete.vue')['default'] AvatarIcon: typeof import('./src/components/Icons/AvatarIcon.vue')['default'] BrandLogo: typeof import('./src/components/BrandLogo.vue')['default'] - BrandSettings: typeof import('./src/components/Settings/General/BrandSettings.vue')['default'] + BrandSettings: typeof import('./src/components/Settings/BrandSettings.vue')['default'] BulkDeleteLinkedDocModal: typeof import('./src/components/BulkDeleteLinkedDocModal.vue')['default'] CalendarEventPanel: typeof import('./src/components/Calendar/CalendarEventPanel.vue')['default'] CalendarIcon: typeof import('./src/components/Icons/CalendarIcon.vue')['default'] @@ -77,7 +77,7 @@ declare module 'vue' { CountUpTimer: typeof import('./src/components/CountUpTimer.vue')['default'] CreateDocumentModal: typeof import('./src/components/Modals/CreateDocumentModal.vue')['default'] CRMLogo: typeof import('./src/components/Icons/CRMLogo.vue')['default'] - CurrencySettings: typeof import('./src/components/Settings/General/CurrencySettings.vue')['default'] + CurrencySettings: typeof import('./src/components/Settings/CurrencySettings.vue')['default'] CustomActions: typeof import('./src/components/CustomActions.vue')['default'] DashboardGrid: typeof import('./src/components/Dashboard/DashboardGrid.vue')['default'] DashboardIcon: typeof import('./src/components/Icons/DashboardIcon.vue')['default'] @@ -145,11 +145,10 @@ declare module 'vue' { FileVideoIcon: typeof import('./src/components/Icons/FileVideoIcon.vue')['default'] Filter: typeof import('./src/components/Filter.vue')['default'] FilterIcon: typeof import('./src/components/Icons/FilterIcon.vue')['default'] + ForecastingSettings: typeof import('./src/components/Settings/ForecastingSettings.vue')['default'] FormattedInput: typeof import('./src/components/Controls/FormattedInput.vue')['default'] FrappeCloudIcon: typeof import('./src/components/Icons/FrappeCloudIcon.vue')['default'] GenderIcon: typeof import('./src/components/Icons/GenderIcon.vue')['default'] - GeneralSettings: typeof import('./src/components/Settings/General/GeneralSettings.vue')['default'] - GeneralSettingsPage: typeof import('./src/components/Settings/General/GeneralSettingsPage.vue')['default'] GlobalModals: typeof import('./src/components/Modals/GlobalModals.vue')['default'] GoogleIcon: typeof import('./src/components/Icons/GoogleIcon.vue')['default'] Grid: typeof import('./src/components/Controls/Grid.vue')['default'] @@ -162,7 +161,7 @@ declare module 'vue' { HelpdeskIcon: typeof import('./src/components/Icons/HelpdeskIcon.vue')['default'] HelpdeskSettings: typeof import('./src/components/Settings/HelpdeskSettings.vue')['default'] HelpIcon: typeof import('./src/components/Icons/HelpIcon.vue')['default'] - HomeActions: typeof import('./src/components/Settings/General/HomeActions.vue')['default'] + HomeActions: typeof import('./src/components/Settings/HomeActions.vue')['default'] Icon: typeof import('./src/components/Icon.vue')['default'] IconPicker: typeof import('./src/components/IconPicker.vue')['default'] ImageUploader: typeof import('./src/components/Controls/ImageUploader.vue')['default'] @@ -253,6 +252,7 @@ declare module 'vue' { SmileIcon: typeof import('./src/components/Icons/SmileIcon.vue')['default'] SortBy: typeof import('./src/components/SortBy.vue')['default'] SortIcon: typeof import('./src/components/Icons/SortIcon.vue')['default'] + SparkleIcon: typeof import('./src/components/Icons/SparkleIcon.vue')['default'] SquareAsterisk: typeof import('./src/components/Icons/SquareAsterisk.vue')['default'] StepsIcon: typeof import('./src/components/Icons/StepsIcon.vue')['default'] SuccessIcon: typeof import('./src/components/Icons/SuccessIcon.vue')['default'] diff --git a/frontend/src/components/ConditionsFilter/CFCondition.vue b/frontend/src/components/ConditionsFilter/CFCondition.vue index 943ad231..4f1411af 100644 --- a/frontend/src/components/ConditionsFilter/CFCondition.vue +++ b/frontend/src/components/ConditionsFilter/CFCondition.vue @@ -110,7 +110,6 @@ diff --git a/frontend/src/components/Icons/SparkleIcon.vue b/frontend/src/components/Icons/SparkleIcon.vue new file mode 100644 index 00000000..33ef2bc2 --- /dev/null +++ b/frontend/src/components/Icons/SparkleIcon.vue @@ -0,0 +1,16 @@ + diff --git a/frontend/src/components/Settings/AssignmentRules/AssigneeRules.vue b/frontend/src/components/Settings/AssignmentRules/AssigneeRules.vue index 78bc4938..c5a83322 100644 --- a/frontend/src/components/Settings/AssignmentRules/AssigneeRules.vue +++ b/frontend/src/components/Settings/AssignmentRules/AssigneeRules.vue @@ -6,10 +6,7 @@ }} {{ - __( - 'Define who receives the {0} and how they’re distributed among agents.', - [documentType], - ) + __('Choose how {0} are assigned among salespeople.', [documentType]) }} @@ -26,7 +23,7 @@
{{ - __('Choose how {0} are distributed among selected assignees.', [ + __('Choose how {0} are assigned among the selected assignees.', [ documentType, ]) }} @@ -36,7 +33,7 @@