Repository: frappe/crm
Pull request #726: fix: export logic
Author: login: pratikb64
Labels: name: backport main-hotfix
Created: 2025-04-08T09:41:30Z | Merged: 2025-04-14T05:49:59Z

Fixed export logic where while exporting data when user selects rows from list view only those rows are exported instead of all

Fixes #673 

Linked issue #673: Wrong checkbox "Export all" logic
When exporting a list of documents (all sections except "Notes" can be exported) to Excel or CSV, the "number of records" checkbox in the pop-up window does not work. If you click it or do not click it, in both cases all documents are downloaded. 

What is the idea? Maybe should appear additional filter after checkbox "Export All" is empty?
Linked issue comments: user: login: michellealva
created_at: 2025-03-20T06:18:23Z
body: @shariquerik if i select any 2 records from the list, and export it (without selecting export all), it exports the first 20.. seems like a bug.
author_association: COLLABORATOR
user: login: shariquerik
created_at: 2025-04-22T10:44:29Z
body: :tada: This issue has been resolved in version 1.41.0 :tada:

The release is available on [GitHub release](https://github.com/frappe/crm/releases/tag/v1.41.0)

Your **[semantic-release](https://github.com/semantic-release/semantic-release)** bot :package::rocket:
author_association: MEMBER

Diff:
diff --git a/frontend/src/components/ListViews/CallLogsListView.vue b/frontend/src/components/ListViews/CallLogsListView.vue
index bf04ccf7e8..73d80e2c6b 100644
--- a/frontend/src/components/ListViews/CallLogsListView.vue
+++ b/frontend/src/components/ListViews/CallLogsListView.vue
@@ -10,6 +10,7 @@
     }"
     row-key="name"
     v-bind="$attrs"
+    @update:selections="(selections) => emit('selectionsChanged', selections)"
   >
     <ListHeader
       class="sm:mx-5 mx-3"
@@ -205,6 +206,7 @@ const emit = defineEmits([
   'applyFilter',
   'applyLikeFilter',
   'likeDoc',
+  'selectionsChanged',
 ])
 
 const pageLengthCount = defineModel()
diff --git a/frontend/src/components/ListViews/ContactsListView.vue b/frontend/src/components/ListViews/ContactsListView.vue
index aeecb7c862..36a21daa09 100644
--- a/frontend/src/components/ListViews/ContactsListView.vue
+++ b/frontend/src/components/ListViews/ContactsListView.vue
@@ -14,6 +14,7 @@
       resizeColumn: options.resizeColumn,
     }"
     row-key="name"
+    @update:selections="(selections) => emit('selectionsChanged', selections)"
   >
     <ListHeader
       class="mx-3 sm:mx-5"
@@ -201,6 +202,7 @@ const emit = defineEmits([
   'applyFilter',
   'applyLikeFilter',
   'likeDoc',
+  'selectionsChanged',
 ])
 
 const route = useRoute()
diff --git a/frontend/src/components/ListViews/DealsListView.vue b/frontend/src/components/ListViews/DealsListView.vue
index ddd0df5193..9cc2ba579c 100644
--- a/frontend/src/components/ListViews/DealsListView.vue
+++ b/frontend/src/components/ListViews/DealsListView.vue
@@ -14,6 +14,7 @@
       resizeColumn: options.resizeColumn,
     }"
     row-key="name"
+    @update:selections="(selections) => emit('selectionsChanged', selections)"
   >
     <ListHeader
       class="sm:mx-5 mx-3"
@@ -245,6 +246,7 @@ const emit = defineEmits([
   'applyFilter',
   'applyLikeFilter',
   'likeDoc',
+  'selectionsChanged',
 ])
 
 const route = useRoute()
diff --git a/frontend/src/components/ListViews/EmailTemplatesListView.vue b/frontend/src/components/ListViews/EmailTemplatesListView.vue
index 2d937cb8c1..3a642a1372 100644
--- a/frontend/src/components/ListViews/EmailTemplatesListView.vue
+++ b/frontend/src/components/ListViews/EmailTemplatesListView.vue
@@ -9,6 +9,7 @@
       resizeColumn: options.resizeColumn,
     }"
     row-key="name"
+    @update:selections="(selections) => emit('selectionsChanged', selections)"
   >
     <ListHeader
       class="sm:mx-5 mx-3"
@@ -191,6 +192,7 @@ const emit = defineEmits([
   'applyFilter',
   'applyLikeFilter',
   'likeDoc',
+  'selectionsChanged',
 ])
 
 const pageLengthCount = defineModel()
diff --git a/frontend/src/components/ListViews/LeadsListView.vue b/frontend/src/components/ListViews/LeadsListView.vue
index dccfe438e7..401094ef05 100644
--- a/frontend/src/components/ListViews/LeadsListView.vue
+++ b/frontend/src/components/ListViews/LeadsListView.vue
@@ -14,6 +14,7 @@
       resizeColumn: options.resizeColumn,
     }"
     row-key="name"
+    @update:selections="(selections) => emit('selectionsChanged', selections)"
   >
     <ListHeader
       class="sm:mx-5 mx-3"
@@ -250,7 +251,6 @@ const props = defineProps({
     }),
   },
 })
-
 const emit = defineEmits([
   'loadMore',
   'updatePageCount',
@@ -258,6 +258,7 @@ const emit = defineEmits([
   'applyFilter',
   'applyLikeFilter',
   'likeDoc',
+  'selectionsChanged',
 ])
 
 const route = useRoute()
diff --git a/frontend/src/components/ListViews/OrganizationsListView.vue b/frontend/src/components/ListViews/OrganizationsListView.vue
index 8dfc0ae664..7884ba3d9a 100644
--- a/frontend/src/components/ListViews/OrganizationsListView.vue
+++ b/frontend/src/components/ListViews/OrganizationsListView.vue
@@ -13,6 +13,7 @@
       resizeColumn: options.resizeColumn,
     }"
     row-key="name"
+    @update:selections="(selections) => emit('selectionsChanged', selections)"
   >
     <ListHeader
       class="sm:mx-5 mx-3"
@@ -186,6 +187,7 @@ const emit = defineEmits([
   'applyFilter',
   'applyLikeFilter',
   'likeDoc',
+  'selectionsChanged',
 ])
 
 const route = useRoute()
diff --git a/frontend/src/components/ListViews/TasksListView.vue b/frontend/src/components/ListViews/TasksListView.vue
index 7109449f82..71550f55fd 100644
--- a/frontend/src/components/ListViews/TasksListView.vue
+++ b/frontend/src/components/ListViews/TasksListView.vue
@@ -9,6 +9,7 @@
       resizeColumn: options.resizeColumn,
     }"
     row-key="name"
+    @update:selections="(selections) => emit('selectionsChanged', selections)"
   >
     <ListHeader
       class="mx-3 sm:mx-5"
@@ -207,6 +208,7 @@ const emit = defineEmits([
   'applyFilter',
   'applyLikeFilter',
   'likeDoc',
+  'selectionsChanged',
 ])
 
 const pageLengthCount = defineModel()
diff --git a/frontend/src/components/ViewControls.vue b/frontend/src/components/ViewControls.vue
index fdd40323d3..64350421ed 100644
--- a/frontend/src/components/ViewControls.vue
+++ b/frontend/src/components/ViewControls.vue
@@ -545,6 +545,11 @@ function reload() {
 const showExportDialog = ref(false)
 const export_type = ref('Excel')
 const export_all = ref(false)
+const selectedRows = ref([])
+
+function updateSelections(selections) {
+  selectedRows.value = Array.from(selections)
+}
 
 async function exportRows() {
   let fields = JSON.stringify(list.value.data.columns.map((f) => f.key))
@@ -560,7 +565,15 @@ async function exportRows() {
     page_length = list.value.data.total_count
   }
 
-  window.location.href = `/api/method/frappe.desk.reportview.export_query?file_format_type=${export_type.value}&title=${props.doctype}&doctype=${props.doctype}&fields=${fields}&filters=${filters}&order_by=${order_by}&page_length=${page_length}&start=0&view=Report&with_comment_count=1`
+  let url = `/api/method/frappe.desk.reportview.export_query?file_format_type=${export_type.value}&title=${props.doctype}&doctype=${props.doctype}&fields=${fields}&filters=${filters}&order_by=${order_by}&page_length=${page_length}&start=0&view=Report&with_comment_count=1`
+
+  // Add selected items parameter if rows are selected
+  if (selectedRows.value?.length && !export_all.value) {
+    url += `&selected_items=${JSON.stringify(selectedRows.value)}`
+  }
+
+  window.location.href = url
+
   showExportDialog.value = false
   export_all.value = false
   export_type.value = 'Excel'
@@ -1336,6 +1349,7 @@ defineExpose({
   viewActions,
   viewsDropdownOptions,
   currentView,
+  updateSelections,
 })
 
 // Watchers
diff --git a/frontend/src/pages/CallLogs.vue b/frontend/src/pages/CallLogs.vue
index 29ea5f01ce..c39c0019ea 100644
--- a/frontend/src/pages/CallLogs.vue
+++ b/frontend/src/pages/CallLogs.vue
@@ -41,6 +41,9 @@
     @applyFilter="(data) => viewControls.applyFilter(data)"
     @applyLikeFilter="(data) => viewControls.applyLikeFilter(data)"
     @likeDoc="(data) => viewControls.likeDoc(data)"
+    @selectionsChanged="
+      (selections) => viewControls.updateSelections(selections)
+    "
   />
   <div
     v-else-if="callLogs.data"
diff --git a/frontend/src/pages/Contacts.vue b/frontend/src/pages/Contacts.vue
index b2de02e638..905a00b578 100644
--- a/frontend/src/pages/Contacts.vue
+++ b/frontend/src/pages/Contacts.vue
@@ -44,6 +44,9 @@
     @applyFilter="(data) => viewControls.applyFilter(data)"
     @applyLikeFilter="(data) => viewControls.applyLikeFilter(data)"
     @likeDoc="(data) => viewControls.likeDoc(data)"
+    @selectionsChanged="
+      (selections) => viewControls.updateSelections(selections)
+    "
   />
   <div
     v-else-if="contacts.data"
diff --git a/frontend/src/pages/Deals.vue b/frontend/src/pages/Deals.vue
index 093acddffe..9da55bad97 100644
--- a/frontend/src/pages/Deals.vue
+++ b/frontend/src/pages/Deals.vue
@@ -223,6 +223,9 @@
     @applyFilter="(data) => viewControls.applyFilter(data)"
     @applyLikeFilter="(data) => viewControls.applyLikeFilter(data)"
     @likeDoc="(data) => viewControls.likeDoc(data)"
+    @selectionsChanged="
+      (selections) => viewControls.updateSelections(selections)
+    "
   />
   <div v-else-if="deals.data" class="flex h-full items-center justify-center">
     <div
diff --git a/frontend/src/pages/EmailTemplates.vue b/frontend/src/pages/EmailTemplates.vue
index 4ab8e0dc57..ebede473e6 100644
--- a/frontend/src/pages/EmailTemplates.vue
+++ b/frontend/src/pages/EmailTemplates.vue
@@ -45,6 +45,9 @@
     @applyFilter="(data) => viewControls.applyFilter(data)"
     @applyLikeFilter="(data) => viewControls.applyLikeFilter(data)"
     @likeDoc="(data) => viewControls.likeDoc(data)"
+    @selectionsChanged="
+      (selections) => viewControls.updateSelections(selections)
+    "
   />
   <div
     v-else-if="emailTemplates.data"
diff --git a/frontend/src/pages/Leads.vue b/frontend/src/pages/Leads.vue
index 3956fb8e77..d1141c92d4 100644
--- a/frontend/src/pages/Leads.vue
+++ b/frontend/src/pages/Leads.vue
@@ -249,6 +249,9 @@
     @applyFilter="(data) => viewControls.applyFilter(data)"
     @applyLikeFilter="(data) => viewControls.applyLikeFilter(data)"
     @likeDoc="(data) => viewControls.likeDoc(data)"
+    @selectionsChanged="
+      (selections) => viewControls.updateSelections(selections)
+    "
   />
   <div v-else-if="leads.data" class="flex h-full items-center justify-center">
     <div
diff --git a/frontend/src/pages/Organizations.vue b/frontend/src/pages/Organizations.vue
index 282411b228..442f98c833 100644
--- a/frontend/src/pages/Organizations.vue
+++ b/frontend/src/pages/Organizations.vue
@@ -44,6 +44,9 @@
     @applyFilter="(data) => viewControls.applyFilter(data)"
     @applyLikeFilter="(data) => viewControls.applyLikeFilter(data)"
     @likeDoc="(data) => viewControls.likeDoc(data)"
+    @selectionsChanged="
+      (selections) => viewControls.updateSelections(selections)
+    "
   />
   <div
     v-else-if="organizations.data"
diff --git a/frontend/src/pages/Tasks.vue b/frontend/src/pages/Tasks.vue
index 894f96d8bd..f73e1804d9 100644
--- a/frontend/src/pages/Tasks.vue
+++ b/frontend/src/pages/Tasks.vue
@@ -172,6 +172,9 @@
     @applyFilter="(data) => viewControls.applyFilter(data)"
     @applyLikeFilter="(data) => viewControls.applyLikeFilter(data)"
     @likeDoc="(data) => viewControls.likeDoc(data)"
+    @selectionsChanged="
+      (selections) => viewControls.updateSelections(selections)
+    "
   />
   <div v-else-if="tasks.data" class="flex h-full items-center justify-center">
     <div


Reviews:
user: login: shariquerik
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-10T06:14:51Z
user: login: shariquerik
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-04-11T14:58:14Z
user: login: pratikb64
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-14T05:22:12Z

Review comments:
diff_hunk: @@ -545,6 +545,11 @@ function reload() {
 const showExportDialog = ref(false)
 const export_type = ref('Excel')
 const export_all = ref(false)
+const selectedRows = ref([])
+
+function handleSelectionsChange(selections) {
path: frontend/src/components/ViewControls.vue
user: login: shariquerik
body: better name could be `updateSelections`
created_at: 2025-04-10T06:14:30Z
author_association: MEMBER
diff_hunk: @@ -560,7 +565,15 @@ async function exportRows() {
     page_length = list.value.data.total_count
   }
 
-  window.location.href = `/api/method/frappe.desk.reportview.export_query?file_format_type=${export_type.value}&title=${props.doctype}&doctype=${props.doctype}&fields=${fields}&filters=${filters}&order_by=${order_by}&page_length=${page_length}&start=0&view=Report&with_comment_count=1`
+  let url = `/api/method/frappe.desk.reportview.export_query?file_format_type=${export_type.value}&title=${props.doctype}&doctype=${props.doctype}&fields=${fields}&filters=${filters}&order_by=${order_by}&page_length=${page_length}&start=0&view=Report&with_comment_count=1`
+
+  // Add selected items parameter if rows are selected
+  if (selectedRows.value?.length && !export_all.value) {
+    url += `&selected_items=${JSON.stringify(selectedRows.value)}`
+  }
path: frontend/src/components/ViewControls.vue
user: login: shariquerik
body: This will not work since selectedRows is a Set
created_at: 2025-04-11T14:58:10Z
author_association: MEMBER
diff_hunk: @@ -560,7 +565,15 @@ async function exportRows() {
     page_length = list.value.data.total_count
   }
 
-  window.location.href = `/api/method/frappe.desk.reportview.export_query?file_format_type=${export_type.value}&title=${props.doctype}&doctype=${props.doctype}&fields=${fields}&filters=${filters}&order_by=${order_by}&page_length=${page_length}&start=0&view=Report&with_comment_count=1`
+  let url = `/api/method/frappe.desk.reportview.export_query?file_format_type=${export_type.value}&title=${props.doctype}&doctype=${props.doctype}&fields=${fields}&filters=${filters}&order_by=${order_by}&page_length=${page_length}&start=0&view=Report&with_comment_count=1`
+
+  // Add selected items parameter if rows are selected
+  if (selectedRows.value?.length && !export_all.value) {
+    url += `&selected_items=${JSON.stringify(selectedRows.value)}`
+  }
path: frontend/src/components/ViewControls.vue
user: login: pratikb64
body: It works because at line 551 in `updateSelections` function it is getting converted to array
created_at: 2025-04-14T05:22:12Z
author_association: COLLABORATOR

PR comments:
user: login: pratikb64
created_at: 2025-04-08T10:57:39Z
body: @mergifyio refresh
author_association: COLLABORATOR
user: login: mergify[bot]
created_at: 2025-04-08T10:57:44Z
body: > refresh

#### ✅ Pull request refreshed



<!---
DO NOT EDIT
-*- Mergify Payload -*-
{"command": "refresh", "conclusion": "success", "action_is_running": true}
-*- Mergify Payload End -*-
-->
author_association: NONE