How to import DataTable in ERPNext form

Hi,

I’m trying to include DataTable in landed_cost_voucher.js using import DataTable from ‘frappe-datatable’; however it breaks the form completely.

form_viewers.js:78 Uncaught TypeError: Cannot read property 'refresh' of undefined
    at Object.frappe.ui.form.set_viewers (form_viewers.js:78)
    at n.<anonymous> (formview.js:41)
    at n.emit (libs.min.js?ver=1534367266.0:7306)
    at n.onevent (libs.min.js?ver=1534367266.0:7307)
    at n.onpacket (libs.min.js?ver=1534367266.0:7307)
    at n.<anonymous> (libs.min.js?ver=1534367266.0:7307)
    at n.emit (libs.min.js?ver=1534367266.0:7306)
    at n.ondecoded (libs.min.js?ver=1534367266.0:7306)
    at s.<anonymous> (libs.min.js?ver=1534367266.0:7307)
    at s.n.emit (libs.min.js?ver=1534367266.0:7306)

How do I use DataTable in a form?

Share your full source!

@rmehta the full source code is not necessary to reproduce this problem. Simply add the one line “import DataTable from ‘frappe-datatable’;” anywhere in landed_cost_voucher.js and it’ll stop functioning properly.

In my case I added the line at the top:

// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
import DataTable from 'frappe-datatable';

frappe.provide("erpnext.stock");

erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
	setup: function() {
		var me = this;
		this.frm.fields_dict.purchase_receipts.grid.get_field('receipt_document').get_query =
			function (doc, cdt, cdn) {
				var d = locals[cdt][cdn]

				var filters = [
					[d.receipt_document_type, 'docstatus', '=', '1'],
					[d.receipt_document_type, 'company', '=', me.frm.doc.company],
				]

				if (d.receipt_document_type == "Purchase Invoice") {
					filters.push(["Purchase Invoice", "update_stock", "=", "1"])
				}

				if (!me.frm.doc.company) frappe.msgprint(__("Please enter company first"));
				return {
					filters: filters
				}
			};

		this.frm.add_fetch("receipt_document", "supplier", "supplier");
		this.frm.add_fetch("receipt_document", "posting_date", "posting_date");
		this.frm.add_fetch("receipt_document", "base_grand_total", "grand_total");

	},

	refresh: function(frm) {
		var help_content =
			`<br><br>
			<table class="table table-bordered" style="background-color: #f9f9f9;">
				<tr><td>
					<h4>
						<i class="fa fa-hand-right"></i> 
						${__("Notes")}:
					</h4>
					<ul>
						<li>
							${__("Charges will be distributed proportionately based on item qty or amount, as per your selection")}
						</li>
						<li>
							${__("Remove item if charges is not applicable to that item")}
						</li>
						<li>
							${__("Charges are updated in Purchase Receipt against each item")}
						</li>
						<li>
							${__("Item valuation rate is recalculated considering landed cost voucher amount")}
						</li>
						<li>
							${__("Stock Ledger Entries and GL Entries are reposted for the selected Purchase Receipts")}
						</li>
					</ul>
				</td></tr>
			</table>`;

		set_field_options("landed_cost_help", help_content);
	},

	get_items_from_purchase_receipts: function() {
		var me = this;
		if(!this.frm.doc.purchase_receipts.length) {
			frappe.msgprint(__("Please enter Purchase Receipt first"));
		} else {
			return this.frm.call({
				doc: me.frm.doc,
				method: "get_items_from_purchase_receipts",
				callback: function(r, rt) {
					me.set_applicable_charges_for_item();
				}
			});
		}
	},

	amount: function(frm) {
		this.set_total_taxes_and_charges();
		this.set_applicable_charges_for_item();
	},

	set_total_taxes_and_charges: function() {
		var total_taxes_and_charges = 0.0;
		$.each(this.frm.doc.taxes || [], function(i, d) {
			total_taxes_and_charges += flt(d.amount)
		});
		cur_frm.set_value("total_taxes_and_charges", total_taxes_and_charges);
	},

	set_applicable_charges_for_item: function() {
		var me = this;

		if(this.frm.doc.taxes.length) {
			
			var total_item_cost = 0.0;
			var based_on = this.frm.doc.distribute_charges_based_on.toLowerCase();
			$.each(this.frm.doc.items || [], function(i, d) {
				total_item_cost += flt(d[based_on])
			});

			var total_charges = 0.0;
			$.each(this.frm.doc.items || [], function(i, item) {
				item.applicable_charges = flt(item[based_on]) * flt(me.frm.doc.total_taxes_and_charges) / flt(total_item_cost)			
				item.applicable_charges = flt(item.applicable_charges, precision("applicable_charges", item))
				total_charges += item.applicable_charges
			});

			if (total_charges != this.frm.doc.total_taxes_and_charges){
				var diff = this.frm.doc.total_taxes_and_charges - flt(total_charges)
				this.frm.doc.items.slice(-1)[0].applicable_charges += diff
			}
			refresh_field("items");
		}
	},
	distribute_charges_based_on: function (frm) {
		this.set_applicable_charges_for_item();
	}

});

cur_frm.script_manager.make(erpnext.stock.LandedCostVoucher);

Additionally, all my source code regarding erpnext is available on my fork https://github.com/SaiFi0102/erpnext

I also tried using frappe.require("/assets/frappe/js/lib/frappe-datatable.js"); and still the same error. Any idea?

Try this or something like it.

DataTable is awesome. Landed Cost Voucher is awesome. What are you using datatable for?

1 Like

I tried exactly that. But it makes the form throw up errors. I’m making a manual distribution table of taxes for cases when some taxes are charged in a non standard manner, however, I’ve had to use HTML templates instead of datatable since it isn’t working.

So anyways, solution please

The solution you don’t want is to save a separate copy of datatable. Let me call for reinforcements: @jhk, how’d you do it with the pond sample tool?

1 Like

Using frappe.run_serially worked for me

function load_dependencies() {
    frappe.require(["/assets/frappe/css/frappe-datatable.css",
        "/assets/frappe/js/lib/clusterize.min.js",
        "/assets/frappe/js/lib/Sortable.min.js",
        "/assets/frappe/js/lib/frappe-datatable.js"]);
}

function render_page() {
    var dt = new DataTable(...);
}

frappe.run_serially([
    () => load_dependencies(),
    () => render_page()
]);
3 Likes

Thanks! I’ll try it out

things have changed now, its frappe.DataTable, there’s no need to import anything, it just works out of the box now.

how to use it??
can you show a example?


can you solve?? what is the issue?


i am importing like this