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 ed89b26e..48f009b3 100644
--- a/frontend/components.d.ts
+++ b/frontend/components.d.ts
@@ -33,7 +33,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']
CalendarIcon: typeof import('./src/components/Icons/CalendarIcon.vue')['default']
CallArea: typeof import('./src/components/Activities/CallArea.vue')['default']
@@ -63,7 +63,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']
@@ -127,11 +127,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']
@@ -142,7 +141,7 @@ declare module 'vue' {
GroupByIcon: typeof import('./src/components/Icons/GroupByIcon.vue')['default']
HeartIcon: typeof import('./src/components/Icons/HeartIcon.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']
@@ -229,6 +228,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 @@
@@ -84,7 +81,7 @@
{{ __('Assignees') }}
- {{ __('Choose who receives the {0}.', [documentType]) }}
+ {{ __('Select the assignees for {0}.', [documentType]) }}
diff --git a/frontend/src/components/Settings/AssignmentRules/AssignmentRuleListItem.vue b/frontend/src/components/Settings/AssignmentRules/AssignmentRuleListItem.vue
index 01336adc..9973088e 100644
--- a/frontend/src/components/Settings/AssignmentRules/AssignmentRuleListItem.vue
+++ b/frontend/src/components/Settings/AssignmentRules/AssignmentRuleListItem.vue
@@ -1,25 +1,25 @@
-
+
{{ data.name }}
{{ data.description }}
-
+
-
+
- TemplateOption({
- option: __('Delete'),
- icon: 'trash-2',
- active: props.active,
- onClick: (e) => {
- e.preventDefault()
- e.stopImmediatePropagation()
- isConfirmingDelete.value = true
- },
- }),
+ icon: 'trash-2',
+ onClick: (e) => {
+ e.preventDefault()
+ e.stopImmediatePropagation()
+ isConfirmingDelete.value = true
+ },
condition: () => !isConfirmingDelete.value,
},
{
label: __('Confirm Delete'),
- component: (props) =>
- TemplateOption({
- option: __('Confirm Delete'),
- icon: 'trash-2',
- active: props.active,
- theme: 'danger',
- onClick: () => deleteAssignmentRule(),
- }),
+ icon: 'trash-2',
+ theme: 'red',
+ onClick: () => deleteAssignmentRule(),
condition: () => isConfirmingDelete.value,
},
]
diff --git a/frontend/src/components/Settings/AssignmentRules/AssignmentRuleView.vue b/frontend/src/components/Settings/AssignmentRules/AssignmentRuleView.vue
index f6dce641..67de35e9 100644
--- a/frontend/src/components/Settings/AssignmentRules/AssignmentRuleView.vue
+++ b/frontend/src/components/Settings/AssignmentRules/AssignmentRuleView.vue
@@ -1,15 +1,9 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{
- priorityOptions.find(
- (option) => option.value == assignmentRuleData.priority,
- )?.label
- }}
-
-
-
-
-
-
-
{
- assignmentRuleData.priority = option.value
- togglePopover()
- }
- "
- >
- {{ option.label }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{
- __('Assignment condition')
- }}
-
-
- {{
- __('Choose which {0} are affected by this assignment rule.', [
- documentType,
- ])
- }}
- {{ __('Learn about conditions') }}
-
-
-
-
-
- {{ __('Old Condition') }}
-
-
-
-
-
- {{ assignmentRuleData.assignCondition }}
-
-
-
-
-
-
-
-
-
- {{ __('Conditions for this rule were created from') }}
- {{
- __('desk')
- }}
- {{
- __(
- 'which are not compatible with this UI, you will need to recreate the conditions here if you want to manage and add new conditions from this UI.',
- )
- }}
-
-
+
+
+
-
-
-
+
+
+
+
+
+
+ {{
+ priorityOptions.find(
+ (option) => option.value == assignmentRuleData.priority,
+ )?.label
+ }}
+
+
+
+
+
+
+
{
+ assignmentRuleData.priority = option.value
+ togglePopover()
+ }
+ "
+ >
+ {{ option.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
{{
- __('Unassignment condition')
- }}
-
-
- {{
- __('Choose which {0} are affected by this un-assignment rule.', [
- documentType,
- ])
- }}
- {{ __('Learn about conditions') }}
-
+
+
+
+
{{
+ __('Assignment condition')
+ }}
+
+
+ {{
+ __('Choose which {0} are affected by this assignment rule.', [
+ documentType,
+ ])
+ }}
+ {{ __('Learn about conditions') }}
+
+
+
+
+
+ {{ __('Old Condition') }}
+
+
+
+
+
+ {{ assignmentRuleData.assignCondition }}
+
+
+
+
+
+
+
-
-
-
- {{ __('Old Condition') }}
-
-
-
-
-
- {{ assignmentRuleData.unassignCondition }}
-
-
-
+
+ {{ __('Conditions for this rule were created from') }}
+ {{
+ __('desk')
+ }}
+ {{
+ __(
+ 'which are not compatible with this UI, you will need to recreate the conditions here if you want to manage and add new conditions from this UI.',
+ )
+ }}
+
+
+
+
+
+
-
-
-
- {{ __('Conditions for this rule were created from') }}
- {{
- __('desk')
- }}
- {{
- __(
- 'which are not compatible with this UI, you will need to recreate the conditions here if you want to manage and add new conditions from this UI.',
- )
- }}
-
-
+
+
+
{{
+ __('Unassignment condition')
+ }}
+
+
+ {{
+ __(
+ 'Choose which {0} are affected by this un-assignment rule.',
+ [documentType],
+ )
+ }}
+ {{ __('Learn about conditions') }}
+
+
+
+
+
+ {{ __('Old Condition') }}
+
+
+
+
+
+ {{ assignmentRuleData.unassignCondition }}
+
+
+
+
+
+
+
+
+
+ {{ __('Conditions for this rule were created from') }}
+
+ {{ __('desk') }}
+
+ {{
+ __(
+ 'which are not compatible with this UI, you will need to recreate the conditions here if you want to manage and add new conditions from this UI.',
+ )
+ }}
+
+
+
+
-
+
+
+
+ {{
+ __('Assignment Schedule')
+ }}
+
+ {{
+ __('Choose the days of the week when this rule should be active.')
+ }}
+
+
+
+
+
+
-
-
-
- {{
- __('Assignment Schedule')
- }}
-
- {{
- __('Choose the days of the week when this rule should be active.')
- }}
-
-
-
-
-
-
+
+
+
-
-
-
-
+
+
+
+
+
{{ __('Assignment rules') }}
-
-
+
+
{{
__(
- 'Assignment Rules automatically route leads or deals to the right team members based on predefined conditions.',
+ 'Assignment rules automatically assign lead/deal to the right sales user based on predefined conditions',
)
}}
-
+
+
+
+
+
+
+
-
-
diff --git a/frontend/src/components/Settings/General/CurrencySettings.vue b/frontend/src/components/Settings/CurrencySettings.vue
similarity index 82%
rename from frontend/src/components/Settings/General/CurrencySettings.vue
rename to frontend/src/components/Settings/CurrencySettings.vue
index 2b3f2559..6780a32a 100644
--- a/frontend/src/components/Settings/General/CurrencySettings.vue
+++ b/frontend/src/components/Settings/CurrencySettings.vue
@@ -1,27 +1,20 @@
-
-
-
emit('updateStep', 'general-settings')"
- class="cursor-pointer hover:bg-transparent focus:bg-transparent focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:none active:bg-transparent active:outline-none active:ring-0 active:ring-offset-0 active:text-ink-gray-5 font-semibold text-xl hover:opacity-70 !justify-start"
- />
-
+
+
+
+ {{ __('Currency & Exchange rate provider') }}
+
+
+ {{
+ __('Configure the currency and exchange rate provider for your CRM')
+ }}
+
-
+
{{ __('Currency') }}
@@ -61,7 +54,7 @@
-
+
{{ __('Exchange rate provider') }}
@@ -131,17 +124,15 @@
diff --git a/frontend/src/components/Settings/EmailTemplate/NewEmailTemplate.vue b/frontend/src/components/Settings/EmailTemplate/NewEmailTemplate.vue
index 699b5c34..2db5bc21 100644
--- a/frontend/src/components/Settings/EmailTemplate/NewEmailTemplate.vue
+++ b/frontend/src/components/Settings/EmailTemplate/NewEmailTemplate.vue
@@ -14,7 +14,11 @@
class="cursor-pointer hover:bg-transparent focus:bg-transparent focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:none active:bg-transparent active:outline-none active:ring-0 active:ring-offset-0 active:text-ink-gray-5 font-semibold text-xl hover:opacity-70 !pr-0 !max-w-96 !justify-start"
/>
-
+
+
+
+ {{ __('Enabled') }}
+
-
(template.enabled = !template.enabled)"
- >
-
{{ __('Enabled') }}
-
-
+
+
+
+ {{ __('Forecasting') }}
+
+
+ {{
+ __(
+ 'Configure forecasting feature to help predict sales performance and growth',
+ )
+ }}
+
+
+
+
+
+
+
+ {{ __('Enable forecasting') }}
+
+
+ {{
+ __(
+ 'Makes "Expected Closure Date" and "Expected Deal Value" mandatory for deal value forecasting',
+ )
+ }}
+
+
+
+
+
+
+
+
+
+
+ {{ __('Auto update expected deal value') }}
+
+
+ {{
+ __(
+ 'Automatically update "Expected Deal Value" based on the total value of associated products in a deal',
+ )
+ }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/Settings/General/GeneralSettings.vue b/frontend/src/components/Settings/General/GeneralSettings.vue
deleted file mode 100644
index 551b05bd..00000000
--- a/frontend/src/components/Settings/General/GeneralSettings.vue
+++ /dev/null
@@ -1,105 +0,0 @@
-
-
-
-
- {{ __('General') }}
-
-
- {{ __('Configure general settings for your CRM') }}
-
-
-
-
-
-
-
-
diff --git a/frontend/src/components/Settings/General/GeneralSettingsPage.vue b/frontend/src/components/Settings/General/GeneralSettingsPage.vue
deleted file mode 100644
index 3b71f1f7..00000000
--- a/frontend/src/components/Settings/General/GeneralSettingsPage.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
diff --git a/frontend/src/components/Settings/General/HomeActions.vue b/frontend/src/components/Settings/HomeActions.vue
similarity index 65%
rename from frontend/src/components/Settings/General/HomeActions.vue
rename to frontend/src/components/Settings/HomeActions.vue
index 72ecc3ea..05f06b8a 100644
--- a/frontend/src/components/Settings/General/HomeActions.vue
+++ b/frontend/src/components/Settings/HomeActions.vue
@@ -1,21 +1,18 @@
-
-
-
emit('updateStep', 'general-settings')"
- class="cursor-pointer hover:bg-transparent focus:bg-transparent focus:outline-none focus:ring-0 focus:ring-offset-0 focus-visible:none active:bg-transparent active:outline-none active:ring-0 active:ring-offset-0 active:text-ink-gray-5 font-semibold text-xl hover:opacity-70 !pr-0 !max-w-96 !justify-start"
- />
+
+
+
+ {{ __('Home actions') }}
+
+
+ {{ __('Configure actions that appear on the home dropdown') }}
+
-
+
-
-
+
+
{{ __('Send invites to') }}
@@ -23,26 +23,21 @@
/>
-
+
-
- {{ __('Invite by email') }}
-
-
-
-
+
diff --git a/frontend/src/components/Settings/Settings.vue b/frontend/src/components/Settings/Settings.vue
index fdbdc549..c1253bf9 100644
--- a/frontend/src/components/Settings/Settings.vue
+++ b/frontend/src/components/Settings/Settings.vue
@@ -7,31 +7,33 @@
>
-
-
+
+
{{ __('Settings') }}
-
-
- {{ __(tab.label) }}
-
-
-
-
+
+
+
+ {{ __(tab.label) }}
+
+
+
+
+
@@ -42,6 +44,9 @@