Skip to content

Commit 8a9c571

Browse files
committed
feat: simple csv import tool (listview)
1 parent f4687aa commit 8a9c571

File tree

8 files changed

+151
-5
lines changed

8 files changed

+151
-5
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,13 @@ A simple Browser Extension that help you work & develop in Frappe/ERPNext Framew
1919
- Copy/Insert any child table all/specific rows from/to any ERPNext site
2020
- Copy/Insert any Customize Form custom fields from/to any ERPNext site
2121
- Copy/Insert Doctypes data between ERPNext Sites
22+
- Simple CSV Files bulk import (usefull also for triggering python db apis, webhooks, server scripts etc)
2223

2324

24-
#### Feel free to contribute in any way to improve the extension if you like it
25+
#### Credits
26+
- [Frappe Framework](https://frappeframework.com/)
27+
- [PapaParse](https://www.papaparse.com/)
28+
2529

2630
## License
2731

content.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,38 @@
1+
function get_fieldname(label, docfields) {
2+
const field = docfields.find((df) => {
3+
if (df.label && df.label === label) {
4+
return df
5+
}
6+
})
7+
return field["fieldname"]
8+
}
9+
10+
async function csvToolReadFile(doctype, docfields, file) {
11+
Papa.parse(file, {
12+
header: true,
13+
transformHeader: function (header, idx) {
14+
if (header === "ID") {
15+
return "docname"
16+
}
17+
return get_fieldname(header, docfields)
18+
},
19+
complete: function (results) {
20+
console.log(results);
21+
let docs = []
22+
23+
for (let i = 0; i < results.data.length; i++) {
24+
let doc = results.data[i]
25+
if (!doc.docname) continue
26+
doc["doctype"] = doctype
27+
28+
docs.push(doc)
29+
}
30+
backgroundMessage('idf_bg_request__csv-tool-bulk_update', docs);
31+
}
32+
});
33+
}
34+
35+
136
// Listen for page scripts
237
window.addEventListener("message", async (event) => {
338
if (event.origin === window.origin) {
@@ -18,6 +53,12 @@ window.addEventListener("message", async (event) => {
1853
case "idf_cs_request__listview_show-insert-doc-data-dialog":
1954
backgroundMessage('idf_bg_request__listview_show-insert-doc-data-dialog', event.data.payload);
2055
break;
56+
case "idf_cs_request__listview_show-csv-tool-dialog":
57+
backgroundMessage('idf_bg_request__listview_show-csv-tool-dialog', event.data.payload);
58+
break;
59+
case "idf_cs_request__csv-tool-read_file":
60+
csvToolReadFile(event.data.payload.doctype, event.data.payload.docfields, event.data.payload.file)
61+
break;
2162
case "idf_cs_request__childtable_save":
2263
backgroundMessage('idf_bg_request__childtable_save', event.data.payload);
2364
break;

examples/bulk-update-speed.webm

1.47 MB
Binary file not shown.

frappe/listview.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ function ListviewSetup(tabId) {
104104
}
105105
});
106106
});
107+
108+
// insert docs
109+
cur_list.page.add_custom_menu_item(idfGroup, "CSV Tool", () => {
110+
postMessage({
111+
eventName: "idf_cs_request__listview_show-csv-tool-dialog",
112+
payload: {
113+
doctype: cur_list.doctype
114+
}
115+
});
116+
});
107117
}
108118
}
109119
, {}, tabId);

frappe/papaparse.min.js

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

logo/logo.png

14.4 KB
Loading

manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"manifest_version": 3,
33
"name": "Frappe/ERPNext Tools",
44
"description": "Simple Extension that help you work & develop with Frappe/ERPNext Framework",
5-
"version": "1.1.5",
5+
"version": "1.1.6",
66
"icons": {
77
"16": "logo/logo.png",
88
"48": "logo/logo.png",
@@ -11,7 +11,7 @@
1111
"permissions": [ "scripting", "storage" ],
1212
"host_permissions": [ "http://*/app/*", "https://*/app/*" ],
1313
"content_scripts": [{
14-
"js": [ "content.js" ],
14+
"js": [ "content.js", "frappe/papaparse.min.js" ],
1515
"matches": [ "http://*/app/*", "https://*/app/*" ],
1616
"run_at": "document_end"
1717
}],

service-worker.js

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,85 @@ async function showInsertDocDataDialog(doctype, bucket = "Current Doc", tabId) {
439439
, args, tabId);
440440
}
441441

442+
async function showCSVToolDialog(payload, tabId) {
443+
let args = {}
444+
idfExec((args) => {
445+
let csv_tool_dialog = new frappe.ui.Dialog({
446+
title: `${window.idfState.idfLogoUrl1} CSV Tool`,
447+
fields: [
448+
{ label: __("Doctype"), fieldtype: "Data", fieldname: "current_doctype", read_only: 1, default: cur_list.doctype },
449+
{ fieldtype: "Column Break" },
450+
{
451+
label: __("Get Import Template"),
452+
fieldtype: "Button",
453+
description: __("Note: Child Table Fields not supported when importing with this tool"),
454+
click: () => {
455+
frappe.new_doc("Data Import", {
456+
reference_doctype: csv_tool_dialog.get_field("current_doctype").value,
457+
import_type: __("Update Existing Records")
458+
}, () => {
459+
setTimeout(() => {
460+
cur_frm.script_manager.trigger("download_template")
461+
}, 2000)
462+
})
463+
464+
465+
}
466+
},
467+
{ label: __("Data Import"), fieldtype: "Section Break" },
468+
{ label: __("Import Type"), fieldtype: "Select", options: ["Create Records", "Update Records"], default: "Update Records", read_only: 1 },
469+
{
470+
fieldtype: "HTML",
471+
fieldname: "file_type",
472+
options: `
473+
<div>
474+
<label for="formFileLg" class="form-label">CSV File</label>
475+
<input class="form-control form-control-lg" id="formFileLg" type="file" accept=".csv">
476+
</div>
477+
`
478+
},
479+
],
480+
primary_action_label: `${window.idfState.idfLogoUrl1} Apply`,
481+
primary_action: async function (values) {
482+
csv_tool_dialog.hide();
483+
let csvFile = document.querySelector("#formFileLg").files[0];
484+
postMessage({
485+
eventName: "idf_cs_request__csv-tool-read_file",
486+
payload: {
487+
doctype: cur_list.doctype,
488+
docfields: frappe.meta.get_docfields(cur_list.doctype).map(df => {
489+
return { label: df.label, fieldname: df.fieldname }
490+
}),
491+
file: csvFile
492+
}
493+
});
494+
frappe.show_alert(`${window.idfState.idfLogoUrl1} in progress, pelase wait...`);
495+
}
496+
})
497+
csv_tool_dialog.show();
498+
}
499+
, args, tabId);
500+
}
501+
502+
async function handleCSVToolBulkUpdate(docs, tabId) {
503+
const args = { docs: docs }
504+
505+
idfExec((args) => {
506+
frappe.call({
507+
method: "frappe.client.bulk_update",
508+
args: {
509+
docs: args.docs
510+
},
511+
callback: function (r) {
512+
console.log(r.message);
513+
frappe.show_alert(`${window.idfState.idfLogoUrl1} Imports (${args.docs.length}), Fails(${r.message.failed_docs.length})`);
514+
}
515+
})
516+
}
517+
, args, tabId);
518+
519+
}
520+
442521
async function saveChildTableData(payload) {
443522
await chrome.storage.local.set({
444523
"storage__childtable_data": payload
@@ -539,7 +618,7 @@ chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
539618
if (!window.idfState.pageInit) {
540619
window.idfState.pageInit = true;
541620

542-
// paching frappe script manager to add forms scripting features
621+
// hooking frappe script manager to add forms scripting features
543622
let oriTrigger = frappe.ui.form.ScriptManager.prototype.trigger;
544623
frappe.ui.form.ScriptManager.prototype.trigger = function (...args) {
545624
setTimeout(() => {
@@ -551,7 +630,7 @@ chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
551630
, 100);
552631
return oriTrigger.call(this, ...args);
553632
}
554-
// paching frappe setup_view to add listview scripting features
633+
// hooking frappe setup_view to add listview scripting features
555634
let oriViewSetup = frappe.views.ListView.prototype.setup_view;
556635
frappe.views.ListView.prototype.setup_view = function (...args) {
557636
setTimeout(() => {
@@ -595,6 +674,11 @@ chrome.runtime.onMessage.addListener(async (event, sender, sendResponse) => {
595674
case "idf_bg_request__listview_show-insert-doc-data-dialog":
596675
showInsertDocDataDialog(event.payload.doctype, event.payload.bucket, tabId);
597676
break;
677+
case "idf_bg_request__listview_show-csv-tool-dialog":
678+
showCSVToolDialog(event.payload, tabId);
679+
break;
680+
case "idf_bg_request__csv-tool-bulk_update":
681+
handleCSVToolBulkUpdate(event.payload, tabId);
598682
case "idf_bg_request__childtable_save":
599683
saveChildTableData(event.payload);
600684
break;

0 commit comments

Comments
 (0)